Commit 5e7f59fa authored by Alexander Lobakin's avatar Alexander Lobakin Committed by Tony Nguyen
Browse files

virtchnl: fix fake 1-elem arrays in structures allocated as `nents + 1`



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: default avatarAlexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Tested-by: default avatarRafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent dd2e84bb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
+3 −3
Original line number Diff line number Diff line
@@ -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.
+19 −25
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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) {
+35 −22
Original line number Diff line number Diff line
@@ -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;
		}