Commit 88eadd91 authored by Stefan Richter's avatar Stefan Richter Committed by Ben Hutchings

firewire: net: guard against rx buffer overflows

commit 667121ac upstream.

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: default avatarEyal Itkin <eyal.itkin@gmail.com>
Reviewed-by: default avatarEyal Itkin <eyal.itkin@gmail.com>
Fixes: CVE 2016-8633
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 400ce4d1
...@@ -591,6 +591,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, ...@@ -591,6 +591,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
int retval; int retval;
u16 ether_type; u16 ether_type;
if (len <= RFC2374_UNFRAG_HDR_SIZE)
return 0;
hdr.w0 = be32_to_cpu(buf[0]); hdr.w0 = be32_to_cpu(buf[0]);
lf = fwnet_get_hdr_lf(&hdr); lf = fwnet_get_hdr_lf(&hdr);
if (lf == RFC2374_HDR_UNFRAG) { if (lf == RFC2374_HDR_UNFRAG) {
...@@ -615,7 +618,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, ...@@ -615,7 +618,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
return fwnet_finish_incoming_packet(net, skb, source_node_id, return fwnet_finish_incoming_packet(net, skb, source_node_id,
is_broadcast, ether_type); is_broadcast, ether_type);
} }
/* A datagram fragment has been received, now the fun begins. */ /* A datagram fragment has been received, now the fun begins. */
if (len <= RFC2374_FRAG_HDR_SIZE)
return 0;
hdr.w1 = ntohl(buf[1]); hdr.w1 = ntohl(buf[1]);
buf += 2; buf += 2;
len -= RFC2374_FRAG_HDR_SIZE; len -= RFC2374_FRAG_HDR_SIZE;
...@@ -629,6 +637,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, ...@@ -629,6 +637,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
datagram_label = fwnet_get_hdr_dgl(&hdr); datagram_label = fwnet_get_hdr_dgl(&hdr);
dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
if (fg_off + len > dg_size)
return 0;
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation); peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation);
...@@ -735,6 +746,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, ...@@ -735,6 +746,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
fw_send_response(card, r, rcode); 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, static void fwnet_receive_broadcast(struct fw_iso_context *context,
u32 cycle, size_t header_length, void *header, void *data) u32 cycle, size_t header_length, void *header, void *data)
{ {
...@@ -744,9 +771,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, ...@@ -744,9 +771,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
__be32 *buf_ptr; __be32 *buf_ptr;
int retval; int retval;
u32 length; u32 length;
u16 source_node_id;
u32 specifier_id;
u32 ver;
unsigned long offset; unsigned long offset;
unsigned long flags; unsigned long flags;
...@@ -763,22 +787,17 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context, ...@@ -763,22 +787,17 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8 if (length > IEEE1394_GASP_HDR_SIZE &&
| (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24; gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
ver = be32_to_cpu(buf_ptr[1]) & 0xffffff; (gasp_version(buf_ptr) == RFC2734_SW_VERSION
source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
if (specifier_id == IANA_SPECIFIER_ID &&
(ver == RFC2734_SW_VERSION
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
|| ver == RFC3146_SW_VERSION || gasp_version(buf_ptr) == RFC3146_SW_VERSION
#endif #endif
)) { ))
buf_ptr += 2; fwnet_incoming_packet(dev, buf_ptr + 2,
length -= IEEE1394_GASP_HDR_SIZE; length - IEEE1394_GASP_HDR_SIZE,
fwnet_incoming_packet(dev, buf_ptr, length, source_node_id, gasp_source_id(buf_ptr),
context->card->generation, true); context->card->generation, true);
}
packet.payload_length = dev->rcv_buffer_size; packet.payload_length = dev->rcv_buffer_size;
packet.interrupt = 1; packet.interrupt = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment