Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 667121ac authored by Stefan Richter's avatar Stefan Richter
Browse files

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: default avatarEyal Itkin <eyal.itkin@gmail.com>
Reviewed-by: default avatarEyal Itkin <eyal.itkin@gmail.com>
Fixes: CVE 2016-8633
Cc: stable@vger.kernel.org
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 6449e31d
Loading
Loading
Loading
Loading
+35 −16
Original line number Original line Diff line number Diff line
@@ -578,6 +578,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) {
@@ -602,7 +605,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;
@@ -616,6 +624,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);
@@ -722,6 +733,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)
{
{
@@ -731,9 +758,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;


@@ -750,22 +774,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;