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>
static struct fwnet_fragment_info *fwnet_frag_new(
	struct fwnet_partial_datagram *pd, unsigned offset, unsigned len)
{
	struct fwnet_fragment_info *fi, *fi2, *new;
	struct list_head *list;

	list = &pd->fi_list;
	list_for_each_entry(fi, &pd->fi_list, fi_link) {
		if (fi->offset + fi->len == offset) {
			/* The new fragment can be tacked on to the end */
			/* Did the new fragment plug a hole? */
			fi2 = list_entry(fi->fi_link.next,
					 struct fwnet_fragment_info, fi_link);
			if (fi->offset + fi->len == fi2->offset) {
				/* glue fragments together */
				fi->len += len + fi2->len;
				list_del(&fi2->fi_link);
				kfree(fi2);
			} else {
				fi->len += len;
			}

			return fi;
		}
		if (offset + len == fi->offset) {
			/* The new fragment can be tacked on to the beginning */
			/* Did the new fragment plug a hole? */
			fi2 = list_entry(fi->fi_link.prev,
					 struct fwnet_fragment_info, fi_link);
			if (fi2->offset + fi2->len == fi->offset) {
				/* glue fragments together */
				fi2->len += fi->len + len;
				list_del(&fi->fi_link);
				kfree(fi);

				return fi2;
			}
			fi->offset = offset;
			fi->len += len;

			return fi;
		}
		if (offset > fi->offset + fi->len) {
			list = &fi->fi_link;
			break;
		}
		if (offset + len < fi->offset) {
			list = fi->fi_link.prev;
			break;
		}
	}

	new = kmalloc(sizeof(*new), GFP_ATOMIC);
	if (!new)
		return NULL;

	new->offset = offset;
	new->len = len;
	list_add(&new->fi_link, list);

	return new;
}

static struct fwnet_fragment_info *fwnet_frag_new(
	struct fwnet_partial_datagram *pd, unsigned offset, unsigned len)
{
	struct fwnet_fragment_info *fi, *fi2, *new;
	struct list_head *list;

	list = &pd->fi_list;
	list_for_each_entry(fi, &pd->fi_list, fi_link) {
		if (fi->offset + fi->len == offset) {
			/* The new fragment can be tacked on to the end */
			/* Did the new fragment plug a hole? */
			fi2 = list_entry(fi->fi_link.next,
					 struct fwnet_fragment_info, fi_link);
			if (fi->offset + fi->len == fi2->offset) {
				/* glue fragments together */
				fi->len += len + fi2->len;
				list_del(&fi2->fi_link);
				kfree(fi2);
			} else {
				fi->len += len;
			}

			return fi;
		}
		if (offset + len == fi->offset) {
			/* The new fragment can be tacked on to the beginning */
			/* Did the new fragment plug a hole? */
			fi2 = list_entry(fi->fi_link.prev,
					 struct fwnet_fragment_info, fi_link);
			if (fi2->offset + fi2->len == fi->offset) {
				/* glue fragments together */
				fi2->len += fi->len + len;
				list_del(&fi->fi_link);
				kfree(fi);

				return fi2;
			}
			fi->offset = offset;
			fi->len += len;

			return fi;
		}
		if (offset > fi->offset + fi->len) {
			list = &fi->fi_link;
			break;
		}
		if (offset + len < fi->offset) {
			list = fi->fi_link.prev;
			break;
		}
	}

	new = kmalloc(sizeof(*new), GFP_ATOMIC);
	if (!new)
		return NULL;

	new->offset = offset;
	new->len = len;
	list_add(&new->fi_link, list);

	return new;
}
