CVE-2017-14497: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: The tpacket_rcv function in net/packet/af_packet.c in the Linux kernel before 4.13 mishandles vnet headers, which might allow local users to cause a denial of service (buffer overflow, and disk and memory corruption) or possibly have unspecified other impact via crafted system calls.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 7.8
Published: 2017-09-15T18:29Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: net/packet/af_packet.c
Commit date: 2017-08-29 15:09:13 -0700
Commit message: packet: Don't write vnet header beyond end of buffer

... which may happen with certain values of tp_reserve and maclen.

Fixes: 58d19b19cd99 ("packet: vnet_hdr support for tpacket_rcv")
Signed-off-by: Benjamin Poirier <bpoirier@suse.com>
Cc: Willem de Bruijn <willemb@google.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Diff: diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 008a45ca31124e..1c61af9af67dae 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2189,10 +2189,11 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 	unsigned short macoff, netoff, hdrlen;
 	struct sk_buff *copy_skb = NULL;
 	struct timespec ts;
 	__u32 ts_status;
 	bool is_drop_n_account = false;
+	bool do_vnet = false;
 
 	/* struct tpacket{2,3}_hdr is aligned to a multiple of TPACKET_ALIGNMENT.
 	 * We may add members to them until current aligned size without forcing
 	 * userspace to call getsockopt(..., PACKET_HDRLEN, ...).
 	 */
@@ -2239,12 +2240,14 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 	} else {
 		unsigned int maclen = skb_network_offset(skb);
 		netoff = TPACKET_ALIGN(po->tp_hdrlen +
 				       (maclen < 16 ? 16 : maclen)) +
 				       po->tp_reserve;
-		if (po->has_vnet_hdr)
+		if (po->has_vnet_hdr) {
 			netoff += sizeof(struct virtio_net_hdr);
+			do_vnet = true;
+		}
 		macoff = netoff - maclen;
 	}
 	if (po->tp_version <= TPACKET_V2) {
 		if (macoff + snaplen > po->rx_ring.frame_size) {
 			if (po->copy_thresh &&
@@ -2257,12 +2260,14 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 				}
 				if (copy_skb)
 					skb_set_owner_r(copy_skb, sk);
 			}
 			snaplen = po->rx_ring.frame_size - macoff;
-			if ((int)snaplen < 0)
+			if ((int)snaplen < 0) {
 				snaplen = 0;
+				do_vnet = false;
+			}
 		}
 	} else if (unlikely(macoff + snaplen >
 			    GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len)) {
 		u32 nval;
 
@@ -2271,10 +2276,11 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 			    snaplen, nval, macoff);
 		snaplen = nval;
 		if (unlikely((int)snaplen < 0)) {
 			snaplen = 0;
 			macoff = GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len;
+			do_vnet = false;
 		}
 	}
 	spin_lock(&sk->sk_receive_queue.lock);
 	h.raw = packet_current_rx_frame(po, skb,
 					TP_STATUS_KERNEL, (macoff+snaplen));
@@ -2296,11 +2302,11 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 		status |= TP_STATUS_COPY;
 		__skb_queue_tail(&sk->sk_receive_queue, copy_skb);
 	}
 	spin_unlock(&sk->sk_receive_queue.lock);
 
-	if (po->has_vnet_hdr) {
+	if (do_vnet) {
 		if (virtio_net_hdr_from_skb(skb, h.raw + macoff -
 					    sizeof(struct virtio_net_hdr),
 					    vio_le(), true)) {
 			spin_lock(&sk->sk_receive_queue.lock);
 			goto drop_n_account;

Vulnerable code: 		if (po->has_vnet_hdr)
			if ((int)snaplen < 0)
	if (po->has_vnet_hdr) {
Fixed code: 	bool do_vnet = false;
		if (po->has_vnet_hdr) {
			do_vnet = true;
		}
			if ((int)snaplen < 0) {
				do_vnet = false;
			}
			do_vnet = false;
	if (do_vnet) {