Olaf Hering 1f77a9
From: "Andrea Parri (Microsoft)" <parri.andrea@gmail.com>
Olaf Hering 1f77a9
Date: Tue, 26 Jan 2021 17:29:07 +0100
Olaf Hering 1f77a9
Patch-mainline: v5.12-rc1
Olaf Hering 1f77a9
Subject: hv_netvsc: Copy packets sent by Hyper-V out of the receive buffer
Olaf Hering 1f77a9
Git-commit: 0ba35fe91ce34f2d0feff626efd0062dac41781c
Olaf Hering 1f77a9
References: bsc#1199364
Olaf Hering 1f77a9
Olaf Hering 1f77a9
Pointers to receive-buffer packets sent by Hyper-V are used within the
Olaf Hering 1f77a9
guest VM.  Hyper-V can send packets with erroneous values or modify
Olaf Hering 1f77a9
packet fields after they are processed by the guest.  To defend against
Olaf Hering 1f77a9
these scenarios, copy (sections of) the incoming packet after validating
Olaf Hering 1f77a9
their length and offset fields in netvsc_filter_receive().  In this way,
Olaf Hering 1f77a9
the packet can no longer be modified by the host.
Olaf Hering 1f77a9
Olaf Hering 1f77a9
Reported-by: Juan Vazquez <juvazq@microsoft.com>
Olaf Hering 1f77a9
Signed-off-by: Andrea Parri (Microsoft) <parri.andrea@gmail.com>
Olaf Hering 1f77a9
Link: https://lore.kernel.org/r/20210126162907.21056-1-parri.andrea@gmail.com
Olaf Hering 1f77a9
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Olaf Hering 1f77a9
Acked-by: Olaf Hering <ohering@suse.de>
Olaf Hering 1f77a9
---
Olaf Hering 1f77a9
 drivers/net/hyperv/hyperv_net.h   | 93 ++++++++++++++++++------------------
Olaf Hering 1f77a9
 drivers/net/hyperv/netvsc.c       | 20 ++++++++
Olaf Hering 1f77a9
 drivers/net/hyperv/netvsc_drv.c   | 24 ++++++----
Olaf Hering 1f77a9
 drivers/net/hyperv/rndis_filter.c | 99 +++++++++++++++++++++++++++------------
Olaf Hering 1f77a9
 4 files changed, 150 insertions(+), 86 deletions(-)
Olaf Hering 1f77a9
Olaf Hering 1f77a9
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
Olaf Hering 1f77a9
--- a/drivers/net/hyperv/hyperv_net.h
Olaf Hering 1f77a9
+++ b/drivers/net/hyperv/hyperv_net.h
Olaf Hering 1f77a9
@@ -105,9 +105,43 @@ struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */
Olaf Hering 1f77a9
 	u32 processor_masks_entry_size;
Olaf Hering 1f77a9
 };
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-/* Fwd declaration */
Olaf Hering 1f77a9
-struct ndis_tcp_ip_checksum_info;
Olaf Hering 1f77a9
-struct ndis_pkt_8021q_info;
Olaf Hering 1f77a9
+struct ndis_tcp_ip_checksum_info {
Olaf Hering 1f77a9
+	union {
Olaf Hering 1f77a9
+		struct {
Olaf Hering 1f77a9
+			u32 is_ipv4:1;
Olaf Hering 1f77a9
+			u32 is_ipv6:1;
Olaf Hering 1f77a9
+			u32 tcp_checksum:1;
Olaf Hering 1f77a9
+			u32 udp_checksum:1;
Olaf Hering 1f77a9
+			u32 ip_header_checksum:1;
Olaf Hering 1f77a9
+			u32 reserved:11;
Olaf Hering 1f77a9
+			u32 tcp_header_offset:10;
Olaf Hering 1f77a9
+		} transmit;
Olaf Hering 1f77a9
+		struct {
Olaf Hering 1f77a9
+			u32 tcp_checksum_failed:1;
Olaf Hering 1f77a9
+			u32 udp_checksum_failed:1;
Olaf Hering 1f77a9
+			u32 ip_checksum_failed:1;
Olaf Hering 1f77a9
+			u32 tcp_checksum_succeeded:1;
Olaf Hering 1f77a9
+			u32 udp_checksum_succeeded:1;
Olaf Hering 1f77a9
+			u32 ip_checksum_succeeded:1;
Olaf Hering 1f77a9
+			u32 loopback:1;
Olaf Hering 1f77a9
+			u32 tcp_checksum_value_invalid:1;
Olaf Hering 1f77a9
+			u32 ip_checksum_value_invalid:1;
Olaf Hering 1f77a9
+		} receive;
Olaf Hering 1f77a9
+		u32  value;
Olaf Hering 1f77a9
+	};
Olaf Hering 1f77a9
+};
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
+struct ndis_pkt_8021q_info {
Olaf Hering 1f77a9
+	union {
Olaf Hering 1f77a9
+		struct {
Olaf Hering 1f77a9
+			u32 pri:3; /* User Priority */
Olaf Hering 1f77a9
+			u32 cfi:1; /* Canonical Format ID */
Olaf Hering 1f77a9
+			u32 vlanid:12; /* VLAN ID */
Olaf Hering 1f77a9
+			u32 reserved:16;
Olaf Hering 1f77a9
+		};
Olaf Hering 1f77a9
+		u32 value;
Olaf Hering 1f77a9
+	};
Olaf Hering 1f77a9
+};
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 /*
Olaf Hering 1f77a9
  * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
Olaf Hering 1f77a9
@@ -194,7 +228,8 @@ int netvsc_send(struct net_device *net,
Olaf Hering 1f77a9
 		struct sk_buff *skb,
Olaf Hering 1f77a9
 		bool xdp_tx);
Olaf Hering 1f77a9
 void netvsc_linkstatus_callback(struct net_device *net,
Olaf Hering 1f77a9
-				struct rndis_message *resp);
Olaf Hering 1f77a9
+				struct rndis_message *resp,
Olaf Hering 1f77a9
+				void *data);
Olaf Hering 1f77a9
 int netvsc_recv_callback(struct net_device *net,
Olaf Hering 1f77a9
 			 struct netvsc_device *nvdev,
Olaf Hering 1f77a9
 			 struct netvsc_channel *nvchan);
Olaf Hering 1f77a9
@@ -884,9 +919,10 @@ struct multi_recv_comp {
Olaf Hering 1f77a9
 #define NVSP_RSC_MAX 562 /* Max #RSC frags in a vmbus xfer page pkt */
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 struct nvsc_rsc {
Olaf Hering 1f77a9
-	const struct ndis_pkt_8021q_info *vlan;
Olaf Hering 1f77a9
-	const struct ndis_tcp_ip_checksum_info *csum_info;
Olaf Hering 1f77a9
-	const u32 *hash_info;
Olaf Hering 1f77a9
+	struct ndis_pkt_8021q_info vlan;
Olaf Hering 1f77a9
+	struct ndis_tcp_ip_checksum_info csum_info;
Olaf Hering 1f77a9
+	u32 hash_info;
Olaf Hering 1f77a9
+	u8 ppi_flags; /* valid/present bits for the above PPIs */
Olaf Hering 1f77a9
 	u8 is_last; /* last RNDIS msg in a vmtransfer_page */
Olaf Hering 1f77a9
 	u32 cnt; /* #fragments in an RSC packet */
Olaf Hering 1f77a9
 	u32 pktlen; /* Full packet length */
Olaf Hering 1f77a9
@@ -894,6 +930,10 @@ struct nvsc_rsc {
Olaf Hering 1f77a9
 	u32 len[NVSP_RSC_MAX];
Olaf Hering 1f77a9
 };
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
+#define NVSC_RSC_VLAN		BIT(0)	/* valid/present bit for 'vlan' */
Olaf Hering 1f77a9
+#define NVSC_RSC_CSUM_INFO	BIT(1)	/* valid/present bit for 'csum_info' */
Olaf Hering 1f77a9
+#define NVSC_RSC_HASH_INFO	BIT(2)	/* valid/present bit for 'hash_info' */
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
 struct netvsc_stats {
Olaf Hering 1f77a9
 	u64 packets;
Olaf Hering 1f77a9
 	u64 bytes;
Olaf Hering 1f77a9
@@ -1002,6 +1042,7 @@ struct net_device_context {
Olaf Hering 1f77a9
 struct netvsc_channel {
Olaf Hering 1f77a9
 	struct vmbus_channel *channel;
Olaf Hering 1f77a9
 	struct netvsc_device *net_device;
Olaf Hering 1f77a9
+	void *recv_buf; /* buffer to copy packets out from the receive buffer */
Olaf Hering 1f77a9
 	const struct vmpacket_descriptor *desc;
Olaf Hering 1f77a9
 	struct napi_struct napi;
Olaf Hering 1f77a9
 	struct multi_send_data msd;
Olaf Hering 1f77a9
@@ -1234,18 +1275,6 @@ struct rndis_pktinfo_id {
Olaf Hering 1f77a9
 	u16 pkt_id;
Olaf Hering 1f77a9
 };
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-struct ndis_pkt_8021q_info {
Olaf Hering 1f77a9
-	union {
Olaf Hering 1f77a9
-		struct {
Olaf Hering 1f77a9
-			u32 pri:3; /* User Priority */
Olaf Hering 1f77a9
-			u32 cfi:1; /* Canonical Format ID */
Olaf Hering 1f77a9
-			u32 vlanid:12; /* VLAN ID */
Olaf Hering 1f77a9
-			u32 reserved:16;
Olaf Hering 1f77a9
-		};
Olaf Hering 1f77a9
-		u32 value;
Olaf Hering 1f77a9
-	};
Olaf Hering 1f77a9
-};
Olaf Hering 1f77a9
-
Olaf Hering 1f77a9
 struct ndis_object_header {
Olaf Hering 1f77a9
 	u8 type;
Olaf Hering 1f77a9
 	u8 revision;
Olaf Hering 1f77a9
@@ -1436,32 +1465,6 @@ struct ndis_offload_params {
Olaf Hering 1f77a9
 	};
Olaf Hering 1f77a9
 };
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-struct ndis_tcp_ip_checksum_info {
Olaf Hering 1f77a9
-	union {
Olaf Hering 1f77a9
-		struct {
Olaf Hering 1f77a9
-			u32 is_ipv4:1;
Olaf Hering 1f77a9
-			u32 is_ipv6:1;
Olaf Hering 1f77a9
-			u32 tcp_checksum:1;
Olaf Hering 1f77a9
-			u32 udp_checksum:1;
Olaf Hering 1f77a9
-			u32 ip_header_checksum:1;
Olaf Hering 1f77a9
-			u32 reserved:11;
Olaf Hering 1f77a9
-			u32 tcp_header_offset:10;
Olaf Hering 1f77a9
-		} transmit;
Olaf Hering 1f77a9
-		struct {
Olaf Hering 1f77a9
-			u32 tcp_checksum_failed:1;
Olaf Hering 1f77a9
-			u32 udp_checksum_failed:1;
Olaf Hering 1f77a9
-			u32 ip_checksum_failed:1;
Olaf Hering 1f77a9
-			u32 tcp_checksum_succeeded:1;
Olaf Hering 1f77a9
-			u32 udp_checksum_succeeded:1;
Olaf Hering 1f77a9
-			u32 ip_checksum_succeeded:1;
Olaf Hering 1f77a9
-			u32 loopback:1;
Olaf Hering 1f77a9
-			u32 tcp_checksum_value_invalid:1;
Olaf Hering 1f77a9
-			u32 ip_checksum_value_invalid:1;
Olaf Hering 1f77a9
-		} receive;
Olaf Hering 1f77a9
-		u32  value;
Olaf Hering 1f77a9
-	};
Olaf Hering 1f77a9
-};
Olaf Hering 1f77a9
-
Olaf Hering 1f77a9
 struct ndis_tcp_lso_info {
Olaf Hering 1f77a9
 	union {
Olaf Hering 1f77a9
 		struct {
Olaf Hering 1f77a9
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
Olaf Hering 1f77a9
--- a/drivers/net/hyperv/netvsc.c
Olaf Hering 1f77a9
+++ b/drivers/net/hyperv/netvsc.c
Olaf Hering 1f77a9
@@ -131,6 +131,7 @@ static void free_netvsc_device(struct rcu_head *head)
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
Olaf Hering 1f77a9
 		xdp_rxq_info_unreg(&nvdev->chan_table[i].xdp_rxq);
Olaf Hering 1f77a9
+		kfree(nvdev->chan_table[i].recv_buf);
Olaf Hering 1f77a9
 		vfree(nvdev->chan_table[i].mrc.slots);
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
@@ -1284,6 +1285,19 @@ static int netvsc_receive(struct net_device *ndev,
Olaf Hering 1f77a9
 			continue;
Olaf Hering 1f77a9
 		}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
+		/* We're going to copy (sections of) the packet into nvchan->recv_buf;
Olaf Hering 1f77a9
+		 * make sure that nvchan->recv_buf is large enough to hold the packet.
Olaf Hering 1f77a9
+		 */
Olaf Hering 1f77a9
+		if (unlikely(buflen > net_device->recv_section_size)) {
Olaf Hering 1f77a9
+			nvchan->rsc.cnt = 0;
Olaf Hering 1f77a9
+			status = NVSP_STAT_FAIL;
Olaf Hering 1f77a9
+			netif_err(net_device_ctx, rx_err, ndev,
Olaf Hering 1f77a9
+				  "Packet too big: buflen=%u recv_section_size=%u\n",
Olaf Hering 1f77a9
+				  buflen, net_device->recv_section_size);
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
+			continue;
Olaf Hering 1f77a9
+		}
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
 		data = recv_buf + offset;
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 		nvchan->rsc.is_last = (i == count - 1);
Olaf Hering 1f77a9
@@ -1535,6 +1549,12 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
Olaf Hering 1f77a9
 	for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
Olaf Hering 1f77a9
 		struct netvsc_channel *nvchan = &net_device->chan_table[i];
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
+		nvchan->recv_buf = kzalloc(device_info->recv_section_size, GFP_KERNEL);
Olaf Hering 1f77a9
+		if (nvchan->recv_buf == NULL) {
Olaf Hering 1f77a9
+			ret = -ENOMEM;
Olaf Hering 1f77a9
+			goto cleanup2;
Olaf Hering 1f77a9
+		}
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
 		nvchan->channel = device->channel;
Olaf Hering 1f77a9
 		nvchan->net_device = net_device;
Olaf Hering 1f77a9
 		u64_stats_init(&nvchan->tx_stats.syncp);
Olaf Hering 1f77a9
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
Olaf Hering 1f77a9
--- a/drivers/net/hyperv/netvsc_drv.c
Olaf Hering 1f77a9
+++ b/drivers/net/hyperv/netvsc_drv.c
Olaf Hering 1f77a9
@@ -743,7 +743,8 @@ static netdev_tx_t netvsc_start_xmit(struct sk_buff *skb,
Olaf Hering 1f77a9
  * netvsc_linkstatus_callback - Link up/down notification
Olaf Hering 1f77a9
  */
Olaf Hering 1f77a9
 void netvsc_linkstatus_callback(struct net_device *net,
Olaf Hering 1f77a9
-				struct rndis_message *resp)
Olaf Hering 1f77a9
+				struct rndis_message *resp,
Olaf Hering 1f77a9
+				void *data)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
 	struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
Olaf Hering 1f77a9
 	struct net_device_context *ndev_ctx = netdev_priv(net);
Olaf Hering 1f77a9
@@ -757,6 +758,9 @@ void netvsc_linkstatus_callback(struct net_device *net,
Olaf Hering 1f77a9
 		return;
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
+	/* Copy the RNDIS indicate status into nvchan->recv_buf */
Olaf Hering 1f77a9
+	memcpy(indicate, data + RNDIS_HEADER_SIZE, sizeof(*indicate));
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
 	/* Update the physical link speed when changing to another vSwitch */
Olaf Hering 1f77a9
 	if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) {
Olaf Hering 1f77a9
 		u32 speed;
Olaf Hering 1f77a9
@@ -771,8 +775,7 @@ void netvsc_linkstatus_callback(struct net_device *net,
Olaf Hering 1f77a9
 			return;
Olaf Hering 1f77a9
 		}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-		speed = *(u32 *)((void *)indicate
Olaf Hering 1f77a9
-				 + indicate->status_buf_offset) / 10000;
Olaf Hering 1f77a9
+		speed = *(u32 *)(data + RNDIS_HEADER_SIZE + indicate->status_buf_offset) / 10000;
Olaf Hering 1f77a9
 		ndev_ctx->speed = speed;
Olaf Hering 1f77a9
 		return;
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
@@ -827,10 +830,11 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
Olaf Hering 1f77a9
 					     struct xdp_buff *xdp)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
 	struct napi_struct *napi = &nvchan->napi;
Olaf Hering 1f77a9
-	const struct ndis_pkt_8021q_info *vlan = nvchan->rsc.vlan;
Olaf Hering 1f77a9
+	const struct ndis_pkt_8021q_info *vlan = &nvchan->rsc.vlan;
Olaf Hering 1f77a9
 	const struct ndis_tcp_ip_checksum_info *csum_info =
Olaf Hering 1f77a9
-						nvchan->rsc.csum_info;
Olaf Hering 1f77a9
-	const u32 *hash_info = nvchan->rsc.hash_info;
Olaf Hering 1f77a9
+						&nvchan->rsc.csum_info;
Olaf Hering 1f77a9
+	const u32 *hash_info = &nvchan->rsc.hash_info;
Olaf Hering 1f77a9
+	u8 ppi_flags = nvchan->rsc.ppi_flags;
Olaf Hering 1f77a9
 	struct sk_buff *skb;
Olaf Hering 1f77a9
 	void *xbuf = xdp->data_hard_start;
Olaf Hering 1f77a9
 	int i;
Olaf Hering 1f77a9
@@ -874,7 +878,7 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
Olaf Hering 1f77a9
 	 * We compute it here if the flags are set, because on Linux, the IP
Olaf Hering 1f77a9
 	 * checksum is always checked.
Olaf Hering 1f77a9
 	 */
Olaf Hering 1f77a9
-	if (csum_info && csum_info->receive.ip_checksum_value_invalid &&
Olaf Hering 1f77a9
+	if ((ppi_flags & NVSC_RSC_CSUM_INFO) && csum_info->receive.ip_checksum_value_invalid &&
Olaf Hering 1f77a9
 	    csum_info->receive.ip_checksum_succeeded &&
Olaf Hering 1f77a9
 	    skb->protocol == htons(ETH_P_IP)) {
Olaf Hering 1f77a9
 		/* Check that there is enough space to hold the IP header. */
Olaf Hering 1f77a9
@@ -886,16 +890,16 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	/* Do L4 checksum offload if enabled and present. */
Olaf Hering 1f77a9
-	if (csum_info && (net->features & NETIF_F_RXCSUM)) {
Olaf Hering 1f77a9
+	if ((ppi_flags & NVSC_RSC_CSUM_INFO) && (net->features & NETIF_F_RXCSUM)) {
Olaf Hering 1f77a9
 		if (csum_info->receive.tcp_checksum_succeeded ||
Olaf Hering 1f77a9
 		    csum_info->receive.udp_checksum_succeeded)
Olaf Hering 1f77a9
 			skb->ip_summed = CHECKSUM_UNNECESSARY;
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-	if (hash_info && (net->features & NETIF_F_RXHASH))
Olaf Hering 1f77a9
+	if ((ppi_flags & NVSC_RSC_HASH_INFO) && (net->features & NETIF_F_RXHASH))
Olaf Hering 1f77a9
 		skb_set_hash(skb, *hash_info, PKT_HASH_TYPE_L4);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-	if (vlan) {
Olaf Hering 1f77a9
+	if (ppi_flags & NVSC_RSC_VLAN) {
Olaf Hering 1f77a9
 		u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT) |
Olaf Hering 1f77a9
 			(vlan->cfi ? VLAN_CFI_MASK : 0);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
Olaf Hering 1f77a9
--- a/drivers/net/hyperv/rndis_filter.c
Olaf Hering 1f77a9
+++ b/drivers/net/hyperv/rndis_filter.c
Olaf Hering 1f77a9
@@ -127,12 +127,13 @@ static void put_rndis_request(struct rndis_device *dev,
Olaf Hering 1f77a9
 }
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 static void dump_rndis_message(struct net_device *netdev,
Olaf Hering 1f77a9
-			       const struct rndis_message *rndis_msg)
Olaf Hering 1f77a9
+			       const struct rndis_message *rndis_msg,
Olaf Hering 1f77a9
+			       const void *data)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
 	switch (rndis_msg->ndis_msg_type) {
Olaf Hering 1f77a9
 	case RNDIS_MSG_PACKET:
Olaf Hering 1f77a9
 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= sizeof(struct rndis_packet)) {
Olaf Hering 1f77a9
-			const struct rndis_packet *pkt = &rndis_msg->msg.pkt;
Olaf Hering 1f77a9
+			const struct rndis_packet *pkt = data + RNDIS_HEADER_SIZE;
Olaf Hering 1f77a9
 			netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
Olaf Hering 1f77a9
 				   "data offset %u data len %u, # oob %u, "
Olaf Hering 1f77a9
 				   "oob offset %u, oob len %u, pkt offset %u, "
Olaf Hering 1f77a9
@@ -152,7 +153,7 @@ static void dump_rndis_message(struct net_device *netdev,
Olaf Hering 1f77a9
 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
Olaf Hering 1f77a9
 				sizeof(struct rndis_initialize_complete)) {
Olaf Hering 1f77a9
 			const struct rndis_initialize_complete *init_complete =
Olaf Hering 1f77a9
-				&rndis_msg->msg.init_complete;
Olaf Hering 1f77a9
+				data + RNDIS_HEADER_SIZE;
Olaf Hering 1f77a9
 			netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
Olaf Hering 1f77a9
 				"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
Olaf Hering 1f77a9
 				"device flags %d, max xfer size 0x%x, max pkts %u, "
Olaf Hering 1f77a9
@@ -173,7 +174,7 @@ static void dump_rndis_message(struct net_device *netdev,
Olaf Hering 1f77a9
 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
Olaf Hering 1f77a9
 				sizeof(struct rndis_query_complete)) {
Olaf Hering 1f77a9
 			const struct rndis_query_complete *query_complete =
Olaf Hering 1f77a9
-				&rndis_msg->msg.query_complete;
Olaf Hering 1f77a9
+				data + RNDIS_HEADER_SIZE;
Olaf Hering 1f77a9
 			netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
Olaf Hering 1f77a9
 				"(len %u, id 0x%x, status 0x%x, buf len %u, "
Olaf Hering 1f77a9
 				"buf offset %u)\n",
Olaf Hering 1f77a9
@@ -188,7 +189,7 @@ static void dump_rndis_message(struct net_device *netdev,
Olaf Hering 1f77a9
 	case RNDIS_MSG_SET_C:
Olaf Hering 1f77a9
 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE + sizeof(struct rndis_set_complete)) {
Olaf Hering 1f77a9
 			const struct rndis_set_complete *set_complete =
Olaf Hering 1f77a9
-				&rndis_msg->msg.set_complete;
Olaf Hering 1f77a9
+				data + RNDIS_HEADER_SIZE;
Olaf Hering 1f77a9
 			netdev_dbg(netdev,
Olaf Hering 1f77a9
 				"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
Olaf Hering 1f77a9
 				rndis_msg->msg_len,
Olaf Hering 1f77a9
@@ -201,7 +202,7 @@ static void dump_rndis_message(struct net_device *netdev,
Olaf Hering 1f77a9
 		if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
Olaf Hering 1f77a9
 				sizeof(struct rndis_indicate_status)) {
Olaf Hering 1f77a9
 			const struct rndis_indicate_status *indicate_status =
Olaf Hering 1f77a9
-				&rndis_msg->msg.indicate_status;
Olaf Hering 1f77a9
+				data + RNDIS_HEADER_SIZE;
Olaf Hering 1f77a9
 			netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
Olaf Hering 1f77a9
 				"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
Olaf Hering 1f77a9
 				rndis_msg->msg_len,
Olaf Hering 1f77a9
@@ -286,8 +287,10 @@ static void rndis_set_link_state(struct rndis_device *rdev,
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 static void rndis_filter_receive_response(struct net_device *ndev,
Olaf Hering 1f77a9
 					  struct netvsc_device *nvdev,
Olaf Hering 1f77a9
-					  const struct rndis_message *resp)
Olaf Hering 1f77a9
+					  struct rndis_message *resp,
Olaf Hering 1f77a9
+					  void *data)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
+	u32 *req_id = &resp->msg.init_complete.req_id;
Olaf Hering 1f77a9
 	struct rndis_device *dev = nvdev->extension;
Olaf Hering 1f77a9
 	struct rndis_request *request = NULL;
Olaf Hering 1f77a9
 	bool found = false;
Olaf Hering 1f77a9
@@ -312,14 +315,16 @@ static void rndis_filter_receive_response(struct net_device *ndev,
Olaf Hering 1f77a9
 		return;
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
+	/* Copy the request ID into nvchan->recv_buf */
Olaf Hering 1f77a9
+	*req_id = *(u32 *)(data + RNDIS_HEADER_SIZE);
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
 	spin_lock_irqsave(&dev->request_lock, flags);
Olaf Hering 1f77a9
 	list_for_each_entry(request, &dev->req_list, list_ent) {
Olaf Hering 1f77a9
 		/*
Olaf Hering 1f77a9
 		 * All request/response message contains RequestId as the 1st
Olaf Hering 1f77a9
 		 * field
Olaf Hering 1f77a9
 		 */
Olaf Hering 1f77a9
-		if (request->request_msg.msg.init_req.req_id
Olaf Hering 1f77a9
-		    == resp->msg.init_complete.req_id) {
Olaf Hering 1f77a9
+		if (request->request_msg.msg.init_req.req_id == *req_id) {
Olaf Hering 1f77a9
 			found = true;
Olaf Hering 1f77a9
 			break;
Olaf Hering 1f77a9
 		}
Olaf Hering 1f77a9
@@ -329,8 +334,10 @@ static void rndis_filter_receive_response(struct net_device *ndev,
Olaf Hering 1f77a9
 	if (found) {
Olaf Hering 1f77a9
 		if (resp->msg_len <=
Olaf Hering 1f77a9
 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
Olaf Hering 1f77a9
-			memcpy(&request->response_msg, resp,
Olaf Hering 1f77a9
-			       resp->msg_len);
Olaf Hering 1f77a9
+			memcpy(&request->response_msg, resp, RNDIS_HEADER_SIZE + sizeof(*req_id));
Olaf Hering 1f77a9
+			memcpy((void *)&request->response_msg + RNDIS_HEADER_SIZE + sizeof(*req_id),
Olaf Hering 1f77a9
+			       data + RNDIS_HEADER_SIZE + sizeof(*req_id),
Olaf Hering 1f77a9
+			       resp->msg_len - RNDIS_HEADER_SIZE - sizeof(*req_id));
Olaf Hering 1f77a9
 			if (request->request_msg.ndis_msg_type ==
Olaf Hering 1f77a9
 			    RNDIS_MSG_QUERY && request->request_msg.msg.
Olaf Hering 1f77a9
 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
Olaf Hering 1f77a9
@@ -359,7 +366,7 @@ static void rndis_filter_receive_response(struct net_device *ndev,
Olaf Hering 1f77a9
 		netdev_err(ndev,
Olaf Hering 1f77a9
 			"no rndis request found for this response "
Olaf Hering 1f77a9
 			"(id 0x%x res type 0x%x)\n",
Olaf Hering 1f77a9
-			resp->msg.init_complete.req_id,
Olaf Hering 1f77a9
+			*req_id,
Olaf Hering 1f77a9
 			resp->ndis_msg_type);
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 }
Olaf Hering 1f77a9
@@ -371,7 +378,7 @@ static void rndis_filter_receive_response(struct net_device *ndev,
Olaf Hering 1f77a9
 static inline void *rndis_get_ppi(struct net_device *ndev,
Olaf Hering 1f77a9
 				  struct rndis_packet *rpkt,
Olaf Hering 1f77a9
 				  u32 rpkt_len, u32 type, u8 internal,
Olaf Hering 1f77a9
-				  u32 ppi_size)
Olaf Hering 1f77a9
+				  u32 ppi_size, void *data)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
 	struct rndis_per_packet_info *ppi;
Olaf Hering 1f77a9
 	int len;
Olaf Hering 1f77a9
@@ -396,6 +403,8 @@ static inline void *rndis_get_ppi(struct net_device *ndev,
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
Olaf Hering 1f77a9
 		rpkt->per_pkt_info_offset);
Olaf Hering 1f77a9
+	/* Copy the PPIs into nvchan->recv_buf */
Olaf Hering 1f77a9
+	memcpy(ppi, data + RNDIS_HEADER_SIZE + rpkt->per_pkt_info_offset, rpkt->per_pkt_info_len);
Olaf Hering 1f77a9
 	len = rpkt->per_pkt_info_len;
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	while (len > 0) {
Olaf Hering 1f77a9
@@ -438,10 +447,29 @@ void rsc_add_data(struct netvsc_channel *nvchan,
Olaf Hering 1f77a9
 	if (cnt) {
Olaf Hering 1f77a9
 		nvchan->rsc.pktlen += len;
Olaf Hering 1f77a9
 	} else {
Olaf Hering 1f77a9
-		nvchan->rsc.vlan = vlan;
Olaf Hering 1f77a9
-		nvchan->rsc.csum_info = csum_info;
Olaf Hering 1f77a9
+		/* The data/values pointed by vlan, csum_info and hash_info are shared
Olaf Hering 1f77a9
+		 * across the different 'fragments' of the RSC packet; store them into
Olaf Hering 1f77a9
+		 * the packet itself.
Olaf Hering 1f77a9
+		 */
Olaf Hering 1f77a9
+		if (vlan != NULL) {
Olaf Hering 1f77a9
+			memcpy(&nvchan->rsc.vlan, vlan, sizeof(*vlan));
Olaf Hering 1f77a9
+			nvchan->rsc.ppi_flags |= NVSC_RSC_VLAN;
Olaf Hering 1f77a9
+		} else {
Olaf Hering 1f77a9
+			nvchan->rsc.ppi_flags &= ~NVSC_RSC_VLAN;
Olaf Hering 1f77a9
+		}
Olaf Hering 1f77a9
+		if (csum_info != NULL) {
Olaf Hering 1f77a9
+			memcpy(&nvchan->rsc.csum_info, csum_info, sizeof(*csum_info));
Olaf Hering 1f77a9
+			nvchan->rsc.ppi_flags |= NVSC_RSC_CSUM_INFO;
Olaf Hering 1f77a9
+		} else {
Olaf Hering 1f77a9
+			nvchan->rsc.ppi_flags &= ~NVSC_RSC_CSUM_INFO;
Olaf Hering 1f77a9
+		}
Olaf Hering 1f77a9
 		nvchan->rsc.pktlen = len;
Olaf Hering 1f77a9
-		nvchan->rsc.hash_info = hash_info;
Olaf Hering 1f77a9
+		if (hash_info != NULL) {
Olaf Hering 1f77a9
+			nvchan->rsc.csum_info = *csum_info;
Olaf Hering 1f77a9
+			nvchan->rsc.ppi_flags |= NVSC_RSC_HASH_INFO;
Olaf Hering 1f77a9
+		} else {
Olaf Hering 1f77a9
+			nvchan->rsc.ppi_flags &= ~NVSC_RSC_HASH_INFO;
Olaf Hering 1f77a9
+		}
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	nvchan->rsc.data[cnt] = data;
Olaf Hering 1f77a9
@@ -453,7 +481,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
Olaf Hering 1f77a9
 				     struct netvsc_device *nvdev,
Olaf Hering 1f77a9
 				     struct netvsc_channel *nvchan,
Olaf Hering 1f77a9
 				     struct rndis_message *msg,
Olaf Hering 1f77a9
-				     u32 data_buflen)
Olaf Hering 1f77a9
+				     void *data, u32 data_buflen)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
Olaf Hering 1f77a9
 	const struct ndis_tcp_ip_checksum_info *csum_info;
Olaf Hering 1f77a9
@@ -461,7 +489,6 @@ static int rndis_filter_receive_data(struct net_device *ndev,
Olaf Hering 1f77a9
 	const struct rndis_pktinfo_id *pktinfo_id;
Olaf Hering 1f77a9
 	const u32 *hash_info;
Olaf Hering 1f77a9
 	u32 data_offset, rpkt_len;
Olaf Hering 1f77a9
-	void *data;
Olaf Hering 1f77a9
 	bool rsc_more = false;
Olaf Hering 1f77a9
 	int ret;
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
@@ -472,6 +499,9 @@ static int rndis_filter_receive_data(struct net_device *ndev,
Olaf Hering 1f77a9
 		return NVSP_STAT_FAIL;
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
+	/* Copy the RNDIS packet into nvchan->recv_buf */
Olaf Hering 1f77a9
+	memcpy(rndis_pkt, data + RNDIS_HEADER_SIZE, sizeof(*rndis_pkt));
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
 	/* Validate rndis_pkt offset */
Olaf Hering 1f77a9
 	if (rndis_pkt->data_offset >= data_buflen - RNDIS_HEADER_SIZE) {
Olaf Hering 1f77a9
 		netdev_err(ndev, "invalid rndis packet offset: %u\n",
Olaf Hering 1f77a9
@@ -497,18 +527,17 @@ static int rndis_filter_receive_data(struct net_device *ndev,
Olaf Hering 1f77a9
 		return NVSP_STAT_FAIL;
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
-	vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan));
Olaf Hering 1f77a9
+	vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan),
Olaf Hering 1f77a9
+			     data);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0,
Olaf Hering 1f77a9
-				  sizeof(*csum_info));
Olaf Hering 1f77a9
+				  sizeof(*csum_info), data);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0,
Olaf Hering 1f77a9
-				  sizeof(*hash_info));
Olaf Hering 1f77a9
+				  sizeof(*hash_info), data);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1,
Olaf Hering 1f77a9
-				   sizeof(*pktinfo_id));
Olaf Hering 1f77a9
-
Olaf Hering 1f77a9
-	data = (void *)msg + data_offset;
Olaf Hering 1f77a9
+				   sizeof(*pktinfo_id), data);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	/* Identify RSC frags, drop erroneous packets */
Olaf Hering 1f77a9
 	if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) {
Olaf Hering 1f77a9
@@ -537,7 +566,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
Olaf Hering 1f77a9
 	 * the data packet to the stack, without the rndis trailer padding
Olaf Hering 1f77a9
 	 */
Olaf Hering 1f77a9
 	rsc_add_data(nvchan, vlan, csum_info, hash_info,
Olaf Hering 1f77a9
-		     data, rndis_pkt->data_len);
Olaf Hering 1f77a9
+		     data + data_offset, rndis_pkt->data_len);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	if (rsc_more)
Olaf Hering 1f77a9
 		return NVSP_STAT_SUCCESS;
Olaf Hering 1f77a9
@@ -559,10 +588,18 @@ int rndis_filter_receive(struct net_device *ndev,
Olaf Hering 1f77a9
 			 void *data, u32 buflen)
Olaf Hering 1f77a9
 {
Olaf Hering 1f77a9
 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
Olaf Hering 1f77a9
-	struct rndis_message *rndis_msg = data;
Olaf Hering 1f77a9
+	struct rndis_message *rndis_msg = nvchan->recv_buf;
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
+	if (buflen < RNDIS_HEADER_SIZE) {
Olaf Hering 1f77a9
+		netdev_err(ndev, "Invalid rndis_msg (buflen: %u)\n", buflen);
Olaf Hering 1f77a9
+		return NVSP_STAT_FAIL;
Olaf Hering 1f77a9
+	}
Olaf Hering 1f77a9
+
Olaf Hering 1f77a9
+	/* Copy the RNDIS msg header into nvchan->recv_buf */
Olaf Hering 1f77a9
+	memcpy(rndis_msg, data, RNDIS_HEADER_SIZE);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	/* Validate incoming rndis_message packet */
Olaf Hering 1f77a9
-	if (buflen < RNDIS_HEADER_SIZE || rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
Olaf Hering 1f77a9
+	if (rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
Olaf Hering 1f77a9
 	    buflen < rndis_msg->msg_len) {
Olaf Hering 1f77a9
 		netdev_err(ndev, "Invalid rndis_msg (buflen: %u, msg_len: %u)\n",
Olaf Hering 1f77a9
 			   buflen, rndis_msg->msg_len);
Olaf Hering 1f77a9
@@ -570,22 +607,22 @@ int rndis_filter_receive(struct net_device *ndev,
Olaf Hering 1f77a9
 	}
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	if (netif_msg_rx_status(net_device_ctx))
Olaf Hering 1f77a9
-		dump_rndis_message(ndev, rndis_msg);
Olaf Hering 1f77a9
+		dump_rndis_message(ndev, rndis_msg, data);
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	switch (rndis_msg->ndis_msg_type) {
Olaf Hering 1f77a9
 	case RNDIS_MSG_PACKET:
Olaf Hering 1f77a9
 		return rndis_filter_receive_data(ndev, net_dev, nvchan,
Olaf Hering 1f77a9
-						 rndis_msg, buflen);
Olaf Hering 1f77a9
+						 rndis_msg, data, buflen);
Olaf Hering 1f77a9
 	case RNDIS_MSG_INIT_C:
Olaf Hering 1f77a9
 	case RNDIS_MSG_QUERY_C:
Olaf Hering 1f77a9
 	case RNDIS_MSG_SET_C:
Olaf Hering 1f77a9
 		/* completion msgs */
Olaf Hering 1f77a9
-		rndis_filter_receive_response(ndev, net_dev, rndis_msg);
Olaf Hering 1f77a9
+		rndis_filter_receive_response(ndev, net_dev, rndis_msg, data);
Olaf Hering 1f77a9
 		break;
Olaf Hering 1f77a9
 
Olaf Hering 1f77a9
 	case RNDIS_MSG_INDICATE:
Olaf Hering 1f77a9
 		/* notification msgs */
Olaf Hering 1f77a9
-		netvsc_linkstatus_callback(ndev, rndis_msg);
Olaf Hering 1f77a9
+		netvsc_linkstatus_callback(ndev, rndis_msg, data);
Olaf Hering 1f77a9
 		break;
Olaf Hering 1f77a9
 	default:
Olaf Hering 1f77a9
 		netdev_err(ndev,