Merge branch 'hv_netvsc-Fix-send-indirection-table-offset'
authorDavid S. Miller <davem@davemloft.net>
Fri, 22 Nov 2019 03:32:23 +0000 (19:32 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 22 Nov 2019 03:32:23 +0000 (19:32 -0800)
Haiyang Zhang says:

====================
hv_netvsc: Fix send indirection table offset

Fix send indirection table offset issues related to guest and
host bugs.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc.c

index 670ef68..fb547f3 100644 (file)
@@ -609,7 +609,8 @@ struct nvsp_5_send_indirect_table {
        /* The number of entries in the send indirection table */
        u32 count;
 
-       /* The offset of the send indirection table from top of this struct.
+       /* The offset of the send indirection table from the beginning of
+        * struct nvsp_message.
         * The send indirection table tells which channel to put the send
         * traffic on. Each entry is a channel number.
         */
index d22a36f..eab83e7 100644 (file)
@@ -1178,20 +1178,39 @@ static int netvsc_receive(struct net_device *ndev,
 }
 
 static void netvsc_send_table(struct net_device *ndev,
-                             const struct nvsp_message *nvmsg)
+                             struct netvsc_device *nvscdev,
+                             const struct nvsp_message *nvmsg,
+                             u32 msglen)
 {
        struct net_device_context *net_device_ctx = netdev_priv(ndev);
-       u32 count, *tab;
+       u32 count, offset, *tab;
        int i;
 
        count = nvmsg->msg.v5_msg.send_table.count;
+       offset = nvmsg->msg.v5_msg.send_table.offset;
+
        if (count != VRSS_SEND_TAB_SIZE) {
                netdev_err(ndev, "Received wrong send-table size:%u\n", count);
                return;
        }
 
-       tab = (u32 *)((unsigned long)&nvmsg->msg.v5_msg.send_table +
-                     nvmsg->msg.v5_msg.send_table.offset);
+       /* If negotiated version <= NVSP_PROTOCOL_VERSION_6, the offset may be
+        * wrong due to a host bug. So fix the offset here.
+        */
+       if (nvscdev->nvsp_version <= NVSP_PROTOCOL_VERSION_6 &&
+           msglen >= sizeof(struct nvsp_message_header) +
+           sizeof(union nvsp_6_message_uber) + count * sizeof(u32))
+               offset = sizeof(struct nvsp_message_header) +
+                        sizeof(union nvsp_6_message_uber);
+
+       /* Boundary check for all versions */
+       if (offset > msglen - count * sizeof(u32)) {
+               netdev_err(ndev, "Received send-table offset too big:%u\n",
+                          offset);
+               return;
+       }
+
+       tab = (void *)nvmsg + offset;
 
        for (i = 0; i < count; i++)
                net_device_ctx->tx_table[i] = tab[i];
@@ -1209,12 +1228,14 @@ static void netvsc_send_vf(struct net_device *ndev,
                    net_device_ctx->vf_alloc ? "added" : "removed");
 }
 
-static  void netvsc_receive_inband(struct net_device *ndev,
-                                  const struct nvsp_message *nvmsg)
+static void netvsc_receive_inband(struct net_device *ndev,
+                                 struct netvsc_device *nvscdev,
+                                 const struct nvsp_message *nvmsg,
+                                 u32 msglen)
 {
        switch (nvmsg->hdr.msg_type) {
        case NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE:
-               netvsc_send_table(ndev, nvmsg);
+               netvsc_send_table(ndev, nvscdev, nvmsg, msglen);
                break;
 
        case NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION:
@@ -1232,6 +1253,7 @@ static int netvsc_process_raw_pkt(struct hv_device *device,
 {
        struct vmbus_channel *channel = nvchan->channel;
        const struct nvsp_message *nvmsg = hv_pkt_data(desc);
+       u32 msglen = hv_pkt_datalen(desc);
 
        trace_nvsp_recv(ndev, channel, nvmsg);
 
@@ -1247,7 +1269,7 @@ static int netvsc_process_raw_pkt(struct hv_device *device,
                break;
 
        case VM_PKT_DATA_INBAND:
-               netvsc_receive_inband(ndev, nvmsg);
+               netvsc_receive_inband(ndev, net_device, nvmsg, msglen);
                break;
 
        default: