virtchnl: fix fake 1-elem arrays in structures allocated as `nents + 1`
authorAlexander Lobakin <aleksander.lobakin@intel.com>
Fri, 28 Jul 2023 15:52:06 +0000 (17:52 +0200)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Wed, 16 Aug 2023 16:05:04 +0000 (09:05 -0700)
There are five virtchnl structures, which are allocated and checked in
the code as `nents + 1`, meaning that they always have memory for one
excessive element regardless of their actual number. This comes from
that their sizeof() includes space for 1 element and then they get
allocated via struct_size() or its open-coded equivalents, passing
the actual number of elements.
Expand virtchnl_struct_size() to handle such structures and replace
those 1-elem arrays with proper flex ones. Also fix several places
which open-code %IAVF_VIRTCHNL_VF_RESOURCE_SIZE. Finally, let the
virtchnl_ether_addr_list size be computed automatically when there's
no enough space for the whole list, otherwise we have to open-code
reverse struct_size() logics.

Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/iavf/iavf.h
drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
drivers/net/ethernet/intel/ice/ice_virtchnl.c
include/linux/avf/virtchnl.h

index 98aca9f8b602d2250233abc0fdf55906ca2d410a..a6983c2d7c303e822716bc68181a31ad1fff8b6b 100644 (file)
@@ -2103,7 +2103,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg)
                goto err;
        }
 
-       len = struct_size(vfres, vsi_res, num_vsis);
+       len = virtchnl_struct_size(vfres, vsi_res, num_vsis);
        vfres = kzalloc(len, GFP_KERNEL);
        if (!vfres) {
                aq_ret = -ENOMEM;
index 8cbdebc5b698959bba0484316f078b2a04eb352a..85fba85fbb232bf869a988892f90084d92b3629a 100644 (file)
@@ -92,9 +92,9 @@ struct iavf_vsi {
 #define IAVF_MBPS_DIVISOR      125000 /* divisor to convert to Mbps */
 #define IAVF_MBPS_QUANTA       50
 
-#define IAVF_VIRTCHNL_VF_RESOURCE_SIZE (sizeof(struct virtchnl_vf_resource) + \
-                                       (IAVF_MAX_VF_VSI * \
-                                        sizeof(struct virtchnl_vsi_resource)))
+#define IAVF_VIRTCHNL_VF_RESOURCE_SIZE                                 \
+       virtchnl_struct_size((struct virtchnl_vf_resource *)NULL,       \
+                            vsi_res, IAVF_MAX_VF_VSI)
 
 /* MAX_MSIX_Q_VECTORS of these are allocated,
  * but we only use one per queue-specific vector.
index 10f03054a603455667f14250add54cb7b512ab30..4fdac698eb38008c5bd80b5bceb16d305b9957eb 100644 (file)
@@ -215,8 +215,7 @@ int iavf_get_vf_config(struct iavf_adapter *adapter)
        u16 len;
        int err;
 
-       len = sizeof(struct virtchnl_vf_resource) +
-               IAVF_MAX_VF_VSI * sizeof(struct virtchnl_vsi_resource);
+       len = IAVF_VIRTCHNL_VF_RESOURCE_SIZE;
        event.buf_len = len;
        event.msg_buf = kzalloc(len, GFP_KERNEL);
        if (!event.msg_buf)
@@ -284,7 +283,7 @@ void iavf_configure_queues(struct iavf_adapter *adapter)
                return;
        }
        adapter->current_op = VIRTCHNL_OP_CONFIG_VSI_QUEUES;
-       len = struct_size(vqci, qpair, pairs);
+       len = virtchnl_struct_size(vqci, qpair, pairs);
        vqci = kzalloc(len, GFP_KERNEL);
        if (!vqci)
                return;
@@ -397,7 +396,7 @@ void iavf_map_queues(struct iavf_adapter *adapter)
 
        q_vectors = adapter->num_msix_vectors - NONQ_VECS;
 
-       len = struct_size(vimi, vecmap, adapter->num_msix_vectors);
+       len = virtchnl_struct_size(vimi, vecmap, adapter->num_msix_vectors);
        vimi = kzalloc(len, GFP_KERNEL);
        if (!vimi)
                return;
@@ -476,13 +475,11 @@ void iavf_add_ether_addrs(struct iavf_adapter *adapter)
        }
        adapter->current_op = VIRTCHNL_OP_ADD_ETH_ADDR;
 
-       len = struct_size(veal, list, count);
+       len = virtchnl_struct_size(veal, list, count);
        if (len > IAVF_MAX_AQ_BUF_SIZE) {
                dev_warn(&adapter->pdev->dev, "Too many add MAC changes in one request\n");
-               count = (IAVF_MAX_AQ_BUF_SIZE -
-                        sizeof(struct virtchnl_ether_addr_list)) /
-                       sizeof(struct virtchnl_ether_addr);
-               len = struct_size(veal, list, count);
+               while (len > IAVF_MAX_AQ_BUF_SIZE)
+                       len = virtchnl_struct_size(veal, list, --count);
                more = true;
        }
 
@@ -547,13 +544,11 @@ void iavf_del_ether_addrs(struct iavf_adapter *adapter)
        }
        adapter->current_op = VIRTCHNL_OP_DEL_ETH_ADDR;
 
-       len = struct_size(veal, list, count);
+       len = virtchnl_struct_size(veal, list, count);
        if (len > IAVF_MAX_AQ_BUF_SIZE) {
                dev_warn(&adapter->pdev->dev, "Too many delete MAC changes in one request\n");
-               count = (IAVF_MAX_AQ_BUF_SIZE -
-                        sizeof(struct virtchnl_ether_addr_list)) /
-                       sizeof(struct virtchnl_ether_addr);
-               len = struct_size(veal, list, count);
+               while (len > IAVF_MAX_AQ_BUF_SIZE)
+                       len = virtchnl_struct_size(veal, list, --count);
                more = true;
        }
        veal = kzalloc(len, GFP_ATOMIC);
@@ -687,12 +682,12 @@ void iavf_add_vlans(struct iavf_adapter *adapter)
 
                adapter->current_op = VIRTCHNL_OP_ADD_VLAN;
 
-               len = sizeof(*vvfl) + (count * sizeof(u16));
+               len = virtchnl_struct_size(vvfl, vlan_id, count);
                if (len > IAVF_MAX_AQ_BUF_SIZE) {
                        dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n");
-                       count = (IAVF_MAX_AQ_BUF_SIZE - sizeof(*vvfl)) /
-                               sizeof(u16);
-                       len = sizeof(*vvfl) + (count * sizeof(u16));
+                       while (len > IAVF_MAX_AQ_BUF_SIZE)
+                               len = virtchnl_struct_size(vvfl, vlan_id,
+                                                          --count);
                        more = true;
                }
                vvfl = kzalloc(len, GFP_ATOMIC);
@@ -838,12 +833,12 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
 
                adapter->current_op = VIRTCHNL_OP_DEL_VLAN;
 
-               len = sizeof(*vvfl) + (count * sizeof(u16));
+               len = virtchnl_struct_size(vvfl, vlan_id, count);
                if (len > IAVF_MAX_AQ_BUF_SIZE) {
                        dev_warn(&adapter->pdev->dev, "Too many delete VLAN changes in one request\n");
-                       count = (IAVF_MAX_AQ_BUF_SIZE - sizeof(*vvfl)) /
-                               sizeof(u16);
-                       len = sizeof(*vvfl) + (count * sizeof(u16));
+                       while (len > IAVF_MAX_AQ_BUF_SIZE)
+                               len = virtchnl_struct_size(vvfl, vlan_id,
+                                                          --count);
                        more = true;
                }
                vvfl = kzalloc(len, GFP_ATOMIC);
@@ -2173,9 +2168,8 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
                }
                break;
        case VIRTCHNL_OP_GET_VF_RESOURCES: {
-               u16 len = sizeof(struct virtchnl_vf_resource) +
-                         IAVF_MAX_VF_VSI *
-                         sizeof(struct virtchnl_vsi_resource);
+               u16 len = IAVF_VIRTCHNL_VF_RESOURCE_SIZE;
+
                memcpy(adapter->vf_res, msg, min(msglen, len));
                iavf_validate_num_queues(adapter);
                iavf_vf_parse_hw_config(&adapter->hw, adapter->vf_res);
index 85d9965315021bcc06d6956ca4b7ff2cdb0a16dd..4a02ed91ba737ca2d96466a65ee8dcdaca6954fc 100644 (file)
@@ -428,7 +428,7 @@ static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg)
                goto err;
        }
 
-       len = sizeof(struct virtchnl_vf_resource);
+       len = virtchnl_struct_size(vfres, vsi_res, 0);
 
        vfres = kzalloc(len, GFP_KERNEL);
        if (!vfres) {
index 3ab207c148095338f1661c6be34b7c1bb1b522f7..c1a20b533fc0d14809a8c97666787e7623681169 100644 (file)
@@ -268,10 +268,11 @@ struct virtchnl_vf_resource {
        u32 rss_key_size;
        u32 rss_lut_size;
 
-       struct virtchnl_vsi_resource vsi_res[1];
+       struct virtchnl_vsi_resource vsi_res[];
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(36, virtchnl_vf_resource);
+VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_vf_resource);
+#define virtchnl_vf_resource_LEGACY_SIZEOF     36
 
 /* VIRTCHNL_OP_CONFIG_TX_QUEUE
  * VF sends this message to set up parameters for one TX queue.
@@ -340,10 +341,11 @@ struct virtchnl_vsi_queue_config_info {
        u16 vsi_id;
        u16 num_queue_pairs;
        u32 pad;
-       struct virtchnl_queue_pair_info qpair[1];
+       struct virtchnl_queue_pair_info qpair[];
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_vsi_queue_config_info);
+#define virtchnl_vsi_queue_config_info_LEGACY_SIZEOF   72
 
 /* VIRTCHNL_OP_REQUEST_QUEUES
  * VF sends this message to request the PF to allocate additional queues to
@@ -385,10 +387,11 @@ VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_vector_map);
 
 struct virtchnl_irq_map_info {
        u16 num_vectors;
-       struct virtchnl_vector_map vecmap[1];
+       struct virtchnl_vector_map vecmap[];
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(14, virtchnl_irq_map_info);
+VIRTCHNL_CHECK_STRUCT_LEN(2, virtchnl_irq_map_info);
+#define virtchnl_irq_map_info_LEGACY_SIZEOF    14
 
 /* VIRTCHNL_OP_ENABLE_QUEUES
  * VIRTCHNL_OP_DISABLE_QUEUES
@@ -459,10 +462,11 @@ VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_ether_addr);
 struct virtchnl_ether_addr_list {
        u16 vsi_id;
        u16 num_elements;
-       struct virtchnl_ether_addr list[1];
+       struct virtchnl_ether_addr list[];
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_ether_addr_list);
+VIRTCHNL_CHECK_STRUCT_LEN(4, virtchnl_ether_addr_list);
+#define virtchnl_ether_addr_list_LEGACY_SIZEOF 12
 
 /* VIRTCHNL_OP_ADD_VLAN
  * VF sends this message to add one or more VLAN tag filters for receives.
@@ -481,10 +485,11 @@ VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_ether_addr_list);
 struct virtchnl_vlan_filter_list {
        u16 vsi_id;
        u16 num_elements;
-       u16 vlan_id[1];
+       u16 vlan_id[];
 };
 
-VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_vlan_filter_list);
+VIRTCHNL_CHECK_STRUCT_LEN(4, virtchnl_vlan_filter_list);
+#define virtchnl_vlan_filter_list_LEGACY_SIZEOF        6
 
 /* This enum is used for all of the VIRTCHNL_VF_OFFLOAD_VLAN_V2_CAPS related
  * structures and opcodes.
@@ -1372,11 +1377,19 @@ VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_fdir_del);
 #define __vss_byone(p, member, count, old)                                   \
        (struct_size(p, member, count) + (old - 1 - struct_size(p, member, 0)))
 
+#define __vss_full(p, member, count, old)                                    \
+       (struct_size(p, member, count) + (old - struct_size(p, member, 0)))
+
 #define __vss(type, func, p, member, count)            \
        struct type: func(p, member, count, type##_LEGACY_SIZEOF)
 
 #define virtchnl_struct_size(p, m, c)                                        \
        _Generic(*p,                                                          \
+                __vss(virtchnl_vf_resource, __vss_full, p, m, c),            \
+                __vss(virtchnl_vsi_queue_config_info, __vss_full, p, m, c),  \
+                __vss(virtchnl_irq_map_info, __vss_full, p, m, c),           \
+                __vss(virtchnl_ether_addr_list, __vss_full, p, m, c),        \
+                __vss(virtchnl_vlan_filter_list, __vss_full, p, m, c),       \
                 __vss(virtchnl_rss_key, __vss_byone, p, m, c),               \
                 __vss(virtchnl_rss_lut, __vss_byone, p, m, c))
 
@@ -1414,24 +1427,23 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
                valid_len = sizeof(struct virtchnl_rxq_info);
                break;
        case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
-               valid_len = sizeof(struct virtchnl_vsi_queue_config_info);
+               valid_len = virtchnl_vsi_queue_config_info_LEGACY_SIZEOF;
                if (msglen >= valid_len) {
                        struct virtchnl_vsi_queue_config_info *vqc =
                            (struct virtchnl_vsi_queue_config_info *)msg;
-                       valid_len += (vqc->num_queue_pairs *
-                                     sizeof(struct
-                                            virtchnl_queue_pair_info));
+                       valid_len = virtchnl_struct_size(vqc, qpair,
+                                                        vqc->num_queue_pairs);
                        if (vqc->num_queue_pairs == 0)
                                err_msg_format = true;
                }
                break;
        case VIRTCHNL_OP_CONFIG_IRQ_MAP:
-               valid_len = sizeof(struct virtchnl_irq_map_info);
+               valid_len = virtchnl_irq_map_info_LEGACY_SIZEOF;
                if (msglen >= valid_len) {
                        struct virtchnl_irq_map_info *vimi =
                            (struct virtchnl_irq_map_info *)msg;
-                       valid_len += (vimi->num_vectors *
-                                     sizeof(struct virtchnl_vector_map));
+                       valid_len = virtchnl_struct_size(vimi, vecmap,
+                                                        vimi->num_vectors);
                        if (vimi->num_vectors == 0)
                                err_msg_format = true;
                }
@@ -1442,23 +1454,24 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
                break;
        case VIRTCHNL_OP_ADD_ETH_ADDR:
        case VIRTCHNL_OP_DEL_ETH_ADDR:
-               valid_len = sizeof(struct virtchnl_ether_addr_list);
+               valid_len = virtchnl_ether_addr_list_LEGACY_SIZEOF;
                if (msglen >= valid_len) {
                        struct virtchnl_ether_addr_list *veal =
                            (struct virtchnl_ether_addr_list *)msg;
-                       valid_len += veal->num_elements *
-                           sizeof(struct virtchnl_ether_addr);
+                       valid_len = virtchnl_struct_size(veal, list,
+                                                        veal->num_elements);
                        if (veal->num_elements == 0)
                                err_msg_format = true;
                }
                break;
        case VIRTCHNL_OP_ADD_VLAN:
        case VIRTCHNL_OP_DEL_VLAN:
-               valid_len = sizeof(struct virtchnl_vlan_filter_list);
+               valid_len = virtchnl_vlan_filter_list_LEGACY_SIZEOF;
                if (msglen >= valid_len) {
                        struct virtchnl_vlan_filter_list *vfl =
                            (struct virtchnl_vlan_filter_list *)msg;
-                       valid_len += vfl->num_elements * sizeof(u16);
+                       valid_len = virtchnl_struct_size(vfl, vlan_id,
+                                                        vfl->num_elements);
                        if (vfl->num_elements == 0)
                                err_msg_format = true;
                }