Commit 801b27e8 authored by Joe Damato's avatar Joe Damato Committed by David S. Miller
Browse files

net: ethtool: Unify ETHTOOL_{G,S}RXFH rxnfc copy



ETHTOOL_GRXFH correctly copies in the full struct ethtool_rxnfc when
FLOW_RSS is set; ETHTOOL_SRXFH needs a similar code path to handle the
FLOW_RSS case so that ethtool can set the flow hash for custom RSS
contexts (if supported by the driver).

The copy code from ETHTOOL_GRXFH has been pulled out in to a helper so
that it can be called in both ETHTOOL_{G,S}RXFH code paths.

Acked-by: default avatarEdward Cree <ecree.xilinx@gmail.com>
Signed-off-by: default avatarJoe Damato <jdamato@fastly.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3d40aed8
Loading
Loading
Loading
Loading
+38 −37
Original line number Diff line number Diff line
@@ -907,6 +907,38 @@ static int ethtool_rxnfc_copy_to_compat(void __user *useraddr,
	return 0;
}

static int ethtool_rxnfc_copy_struct(u32 cmd, struct ethtool_rxnfc *info,
				     size_t *info_size, void __user *useraddr)
{
	/* struct ethtool_rxnfc was originally defined for
	 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
	 * members.  User-space might still be using that
	 * definition.
	 */
	if (cmd == ETHTOOL_GRXFH || cmd == ETHTOOL_SRXFH)
		*info_size = (offsetof(struct ethtool_rxnfc, data) +
			      sizeof(info->data));

	if (ethtool_rxnfc_copy_from_user(info, useraddr, *info_size))
		return -EFAULT;

	if ((cmd == ETHTOOL_GRXFH || cmd == ETHTOOL_SRXFH) && info->flow_type & FLOW_RSS) {
		*info_size = sizeof(*info);
		if (ethtool_rxnfc_copy_from_user(info, useraddr, *info_size))
			return -EFAULT;
		/* Since malicious users may modify the original data,
		 * we need to check whether FLOW_RSS is still requested.
		 */
		if (!(info->flow_type & FLOW_RSS))
			return -EINVAL;
	}

	if (info->cmd != cmd)
		return -EINVAL;

	return 0;
}

static int ethtool_rxnfc_copy_to_user(void __user *useraddr,
				      const struct ethtool_rxnfc *rxnfc,
				      size_t size, const u32 *rule_buf)
@@ -944,16 +976,9 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
	if (!dev->ethtool_ops->set_rxnfc)
		return -EOPNOTSUPP;

	/* struct ethtool_rxnfc was originally defined for
	 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
	 * members.  User-space might still be using that
	 * definition. */
	if (cmd == ETHTOOL_SRXFH)
		info_size = (offsetof(struct ethtool_rxnfc, data) +
			     sizeof(info.data));

	if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
		return -EFAULT;
	rc = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
	if (rc)
		return rc;

	rc = dev->ethtool_ops->set_rxnfc(dev, &info);
	if (rc)
@@ -978,33 +1003,9 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
	if (!ops->get_rxnfc)
		return -EOPNOTSUPP;

	/* struct ethtool_rxnfc was originally defined for
	 * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
	 * members.  User-space might still be using that
	 * definition. */
	if (cmd == ETHTOOL_GRXFH)
		info_size = (offsetof(struct ethtool_rxnfc, data) +
			     sizeof(info.data));

	if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
		return -EFAULT;

	/* If FLOW_RSS was requested then user-space must be using the
	 * new definition, as FLOW_RSS is newer.
	 */
	if (cmd == ETHTOOL_GRXFH && info.flow_type & FLOW_RSS) {
		info_size = sizeof(info);
		if (ethtool_rxnfc_copy_from_user(&info, useraddr, info_size))
			return -EFAULT;
		/* Since malicious users may modify the original data,
		 * we need to check whether FLOW_RSS is still requested.
		 */
		if (!(info.flow_type & FLOW_RSS))
			return -EINVAL;
	}

	if (info.cmd != cmd)
		return -EINVAL;
	ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
	if (ret)
		return ret;

	if (info.cmd == ETHTOOL_GRXCLSRLALL) {
		if (info.rule_cnt > 0) {