diff options
Diffstat (limited to 'net/core/ethtool.c')
| -rw-r--r-- | net/core/ethtool.c | 41 | 
1 files changed, 30 insertions, 11 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index a0f4964033d..75e4ffeb8cc 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -318,23 +318,33 @@ out:  }  static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, -						void __user *useraddr) +						u32 cmd, void __user *useraddr)  { -	struct ethtool_rxnfc cmd; +	struct ethtool_rxnfc info; +	size_t info_size = sizeof(info);  	if (!dev->ethtool_ops->set_rxnfc)  		return -EOPNOTSUPP; -	if (copy_from_user(&cmd, useraddr, sizeof(cmd))) +	/* 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 (copy_from_user(&info, useraddr, info_size))  		return -EFAULT; -	return dev->ethtool_ops->set_rxnfc(dev, &cmd); +	return dev->ethtool_ops->set_rxnfc(dev, &info);  }  static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, -						void __user *useraddr) +						u32 cmd, void __user *useraddr)  {  	struct ethtool_rxnfc info; +	size_t info_size = sizeof(info);  	const struct ethtool_ops *ops = dev->ethtool_ops;  	int ret;  	void *rule_buf = NULL; @@ -342,13 +352,22 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,  	if (!ops->get_rxnfc)  		return -EOPNOTSUPP; -	if (copy_from_user(&info, useraddr, sizeof(info))) +	/* 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 (copy_from_user(&info, useraddr, info_size))  		return -EFAULT;  	if (info.cmd == ETHTOOL_GRXCLSRLALL) {  		if (info.rule_cnt > 0) { -			rule_buf = kmalloc(info.rule_cnt * sizeof(u32), -					   GFP_USER); +			if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) +				rule_buf = kmalloc(info.rule_cnt * sizeof(u32), +						   GFP_USER);  			if (!rule_buf)  				return -ENOMEM;  		} @@ -359,7 +378,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,  		goto err_out;  	ret = -EFAULT; -	if (copy_to_user(useraddr, &info, sizeof(info))) +	if (copy_to_user(useraddr, &info, info_size))  		goto err_out;  	if (rule_buf) { @@ -1516,12 +1535,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)  	case ETHTOOL_GRXCLSRLCNT:  	case ETHTOOL_GRXCLSRULE:  	case ETHTOOL_GRXCLSRLALL: -		rc = ethtool_get_rxnfc(dev, useraddr); +		rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);  		break;  	case ETHTOOL_SRXFH:  	case ETHTOOL_SRXCLSRLDEL:  	case ETHTOOL_SRXCLSRLINS: -		rc = ethtool_set_rxnfc(dev, useraddr); +		rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);  		break;  	case ETHTOOL_GGRO:  		rc = ethtool_get_gro(dev, useraddr);  |