firewire: net: guard against rx buffer overflows
[pandora-kernel.git] / drivers / firewire / net.c
index a20f45b..418c4da 100644 (file)
@@ -677,6 +677,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
        int retval;
        u16 ether_type;
 
+       if (len <= RFC2374_UNFRAG_HDR_SIZE)
+               return 0;
+
        hdr.w0 = be32_to_cpu(buf[0]);
        lf = fwnet_get_hdr_lf(&hdr);
        if (lf == RFC2374_HDR_UNFRAG) {
@@ -702,7 +705,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
                return fwnet_finish_incoming_packet(net, skb, source_node_id,
                                                    is_broadcast, ether_type);
        }
+
        /* A datagram fragment has been received, now the fun begins. */
+
+       if (len <= RFC2374_FRAG_HDR_SIZE)
+               return 0;
+
        hdr.w1 = ntohl(buf[1]);
        buf += 2;
        len -= RFC2374_FRAG_HDR_SIZE;
@@ -716,6 +724,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
        datagram_label = fwnet_get_hdr_dgl(&hdr);
        dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
 
+       if (fg_off + len > dg_size)
+               return 0;
+
        spin_lock_irqsave(&dev->lock, flags);
 
        peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation);
@@ -822,6 +833,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
        fw_send_response(card, r, rcode);
 }
 
+static int gasp_source_id(__be32 *p)
+{
+       return be32_to_cpu(p[0]) >> 16;
+}
+
+static u32 gasp_specifier_id(__be32 *p)
+{
+       return (be32_to_cpu(p[0]) & 0xffff) << 8 |
+              (be32_to_cpu(p[1]) & 0xff000000) >> 24;
+}
+
+static u32 gasp_version(__be32 *p)
+{
+       return be32_to_cpu(p[1]) & 0xffffff;
+}
+
 static void fwnet_receive_broadcast(struct fw_iso_context *context,
                u32 cycle, size_t header_length, void *header, void *data)
 {
@@ -832,9 +859,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
        __be32 *buf_ptr;
        int retval;
        u32 length;
-       u16 source_node_id;
-       u32 specifier_id;
-       u32 ver;
        unsigned long offset;
        unsigned long flags;
 
@@ -852,17 +876,13 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
 
        spin_unlock_irqrestore(&dev->lock, flags);
 
-       specifier_id =    (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8
-                       | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24;
-       ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
-       source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
-
-       if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) {
-               buf_ptr += 2;
-               length -= IEEE1394_GASP_HDR_SIZE;
-               fwnet_incoming_packet(dev, buf_ptr, length,
-                                     source_node_id, -1, true);
-       }
+       if (length > IEEE1394_GASP_HDR_SIZE &&
+           gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
+           gasp_version(buf_ptr) == RFC2734_SW_VERSION)
+               fwnet_incoming_packet(dev, buf_ptr + 2,
+                                     length - IEEE1394_GASP_HDR_SIZE,
+                                     gasp_source_id(buf_ptr),
+                                     context->card->generation, true);
 
        packet.payload_length = dev->rcv_buffer_size;
        packet.interrupt = 1;
@@ -956,7 +976,12 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
                        break;
                }
 
-               skb_pull(skb, ptask->max_payload);
+               if (ptask->dest_node == IEEE1394_ALL_NODES) {
+                       skb_pull(skb,
+                                ptask->max_payload + IEEE1394_GASP_HDR_SIZE);
+               } else {
+                       skb_pull(skb, ptask->max_payload);
+               }
                if (ptask->outstanding_pkts > 1) {
                        fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG,
                                          dg_size, fg_off, datagram_label);
@@ -1059,7 +1084,7 @@ static int fwnet_send_packet(struct fwnet_packet_task *ptask)
                smp_rmb();
                node_id = dev->card->node_id;
 
-               p = skb_push(ptask->skb, 8);
+               p = skb_push(ptask->skb, IEEE1394_GASP_HDR_SIZE);
                put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p);
                put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24
                                                | RFC2734_SW_VERSION, &p[4]);