Commit 7850f63f authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller

ethtool: Centralise validation of ETHTOOL_{G, S}RXFHINDIR parameters

Add a new ethtool operation (get_rxfh_indir_size) to get the
indirectional table size.  Use this to validate the user buffer size
before calling get_rxfh_indir or set_rxfh_indir.  Use get_rxnfc to get
the number of RX rings, and validate the contents of the new
indirection table before calling set_rxfh_indir.  Remove this
validation from drivers.
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Acked-by: default avatarDimitris Michailidis <dm@chelsio.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 14596f70
...@@ -2302,18 +2302,20 @@ static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, ...@@ -2302,18 +2302,20 @@ static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
} }
} }
static int bnx2x_get_rxfh_indir(struct net_device *dev, static u32 bnx2x_get_rxfh_indir_size(struct net_device *dev)
struct ethtool_rxfh_indir *indir) {
struct bnx2x *bp = netdev_priv(dev);
return (bp->multi_mode == ETH_RSS_MODE_DISABLED ?
0 : T_ETH_INDIRECTION_TABLE_SIZE);
}
static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
{ {
struct bnx2x *bp = netdev_priv(dev); struct bnx2x *bp = netdev_priv(dev);
size_t copy_size =
min_t(size_t, indir->size, T_ETH_INDIRECTION_TABLE_SIZE);
u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
size_t i; size_t i;
if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
return -EOPNOTSUPP;
/* Get the current configuration of the RSS indirection table */ /* Get the current configuration of the RSS indirection table */
bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table); bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table);
...@@ -2326,33 +2328,19 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev, ...@@ -2326,33 +2328,19 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev,
* align the returned table to the Client ID of the leading RSS * align the returned table to the Client ID of the leading RSS
* queue. * queue.
*/ */
for (i = 0; i < copy_size; i++) for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++)
indir->ring_index[i] = ind_table[i] - bp->fp->cl_id; indir[i] = ind_table[i] - bp->fp->cl_id;
indir->size = T_ETH_INDIRECTION_TABLE_SIZE;
return 0; return 0;
} }
static int bnx2x_set_rxfh_indir(struct net_device *dev, static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir)
const struct ethtool_rxfh_indir *indir)
{ {
struct bnx2x *bp = netdev_priv(dev); struct bnx2x *bp = netdev_priv(dev);
size_t i; size_t i;
u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
u32 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp);
if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
return -EOPNOTSUPP;
/* validate the size */
if (indir->size != T_ETH_INDIRECTION_TABLE_SIZE)
return -EINVAL;
for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) { for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
/* validate the indices */
if (indir->ring_index[i] >= num_eth_queues)
return -EINVAL;
/* /*
* The same as in bnx2x_get_rxfh_indir: we can't use a memcpy() * The same as in bnx2x_get_rxfh_indir: we can't use a memcpy()
* as an internal storage of an indirection table is a u8 array * as an internal storage of an indirection table is a u8 array
...@@ -2362,7 +2350,7 @@ static int bnx2x_set_rxfh_indir(struct net_device *dev, ...@@ -2362,7 +2350,7 @@ static int bnx2x_set_rxfh_indir(struct net_device *dev,
* align the received table to the Client ID of the leading RSS * align the received table to the Client ID of the leading RSS
* queue * queue
*/ */
ind_table[i] = indir->ring_index[i] + bp->fp->cl_id; ind_table[i] = indir[i] + bp->fp->cl_id;
} }
return bnx2x_config_rss_pf(bp, ind_table, false); return bnx2x_config_rss_pf(bp, ind_table, false);
...@@ -2395,6 +2383,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { ...@@ -2395,6 +2383,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
.set_phys_id = bnx2x_set_phys_id, .set_phys_id = bnx2x_set_phys_id,
.get_ethtool_stats = bnx2x_get_ethtool_stats, .get_ethtool_stats = bnx2x_get_ethtool_stats,
.get_rxnfc = bnx2x_get_rxnfc, .get_rxnfc = bnx2x_get_rxnfc,
.get_rxfh_indir_size = bnx2x_get_rxfh_indir_size,
.get_rxfh_indir = bnx2x_get_rxfh_indir, .get_rxfh_indir = bnx2x_get_rxfh_indir,
.set_rxfh_indir = bnx2x_set_rxfh_indir, .set_rxfh_indir = bnx2x_set_rxfh_indir,
}; };
......
...@@ -1871,30 +1871,30 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features) ...@@ -1871,30 +1871,30 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
return err; return err;
} }
static int get_rss_table(struct net_device *dev, struct ethtool_rxfh_indir *p) static u32 get_rss_table_size(struct net_device *dev)
{ {
const struct port_info *pi = netdev_priv(dev); const struct port_info *pi = netdev_priv(dev);
unsigned int n = min_t(unsigned int, p->size, pi->rss_size);
p->size = pi->rss_size; return pi->rss_size;
}
static int get_rss_table(struct net_device *dev, u32 *p)
{
const struct port_info *pi = netdev_priv(dev);
unsigned int n = pi->rss_size;
while (n--) while (n--)
p->ring_index[n] = pi->rss[n]; p[n] = pi->rss[n];
return 0; return 0;
} }
static int set_rss_table(struct net_device *dev, static int set_rss_table(struct net_device *dev, const u32 *p)
const struct ethtool_rxfh_indir *p)
{ {
unsigned int i; unsigned int i;
struct port_info *pi = netdev_priv(dev); struct port_info *pi = netdev_priv(dev);
if (p->size != pi->rss_size) for (i = 0; i < pi->rss_size; i++)
return -EINVAL; pi->rss[i] = p[i];
for (i = 0; i < p->size; i++)
if (p->ring_index[i] >= pi->nqsets)
return -EINVAL;
for (i = 0; i < p->size; i++)
pi->rss[i] = p->ring_index[i];
if (pi->adapter->flags & FULL_INIT_DONE) if (pi->adapter->flags & FULL_INIT_DONE)
return write_rss(pi, pi->rss); return write_rss(pi, pi->rss);
return 0; return 0;
...@@ -1989,6 +1989,7 @@ static struct ethtool_ops cxgb_ethtool_ops = { ...@@ -1989,6 +1989,7 @@ static struct ethtool_ops cxgb_ethtool_ops = {
.get_wol = get_wol, .get_wol = get_wol,
.set_wol = set_wol, .set_wol = set_wol,
.get_rxnfc = get_rxnfc, .get_rxnfc = get_rxnfc,
.get_rxfh_indir_size = get_rss_table_size,
.get_rxfh_indir = get_rss_table, .get_rxfh_indir = get_rss_table,
.set_rxfh_indir = set_rss_table, .set_rxfh_indir = set_rss_table,
.flash_device = set_flash, .flash_device = set_flash,
......
...@@ -956,40 +956,28 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev, ...@@ -956,40 +956,28 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
return rc < 0 ? rc : 0; return rc < 0 ? rc : 0;
} }
static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
struct ethtool_rxfh_indir *indir)
{ {
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
size_t copy_size =
min_t(size_t, indir->size, ARRAY_SIZE(efx->rx_indir_table));
if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) return (efx_nic_rev(efx) < EFX_REV_FALCON_B0 ?
return -EOPNOTSUPP; 0 : ARRAY_SIZE(efx->rx_indir_table));
}
static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, u32 *indir)
{
struct efx_nic *efx = netdev_priv(net_dev);
indir->size = ARRAY_SIZE(efx->rx_indir_table); memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table));
memcpy(indir->ring_index, efx->rx_indir_table,
copy_size * sizeof(indir->ring_index[0]));
return 0; return 0;
} }
static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
const struct ethtool_rxfh_indir *indir) const u32 *indir)
{ {
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
size_t i;
if (efx_nic_rev(efx) < EFX_REV_FALCON_B0)
return -EOPNOTSUPP;
/* Validate size and indices */
if (indir->size != ARRAY_SIZE(efx->rx_indir_table))
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++)
if (indir->ring_index[i] >= efx->n_rx_channels)
return -EINVAL;
memcpy(efx->rx_indir_table, indir->ring_index, memcpy(efx->rx_indir_table, indir, sizeof(efx->rx_indir_table));
sizeof(efx->rx_indir_table));
efx_nic_push_rx_indir_table(efx); efx_nic_push_rx_indir_table(efx);
return 0; return 0;
} }
...@@ -1020,6 +1008,7 @@ const struct ethtool_ops efx_ethtool_ops = { ...@@ -1020,6 +1008,7 @@ const struct ethtool_ops efx_ethtool_ops = {
.reset = efx_ethtool_reset, .reset = efx_ethtool_reset,
.get_rxnfc = efx_ethtool_get_rxnfc, .get_rxnfc = efx_ethtool_get_rxnfc,
.set_rx_ntuple = efx_ethtool_set_rx_ntuple, .set_rx_ntuple = efx_ethtool_set_rx_ntuple,
.get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
.get_rxfh_indir = efx_ethtool_get_rxfh_indir, .get_rxfh_indir = efx_ethtool_get_rxfh_indir,
.set_rxfh_indir = efx_ethtool_set_rxfh_indir, .set_rxfh_indir = efx_ethtool_set_rxfh_indir,
}; };
...@@ -565,44 +565,38 @@ vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info, ...@@ -565,44 +565,38 @@ vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
} }
#ifdef VMXNET3_RSS #ifdef VMXNET3_RSS
static u32
vmxnet3_get_rss_indir_size(struct net_device *netdev)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
struct UPT1_RSSConf *rssConf = adapter->rss_conf;
return rssConf->indTableSize;
}
static int static int
vmxnet3_get_rss_indir(struct net_device *netdev, vmxnet3_get_rss_indir(struct net_device *netdev, u32 *p)
struct ethtool_rxfh_indir *p)
{ {
struct vmxnet3_adapter *adapter = netdev_priv(netdev); struct vmxnet3_adapter *adapter = netdev_priv(netdev);
struct UPT1_RSSConf *rssConf = adapter->rss_conf; struct UPT1_RSSConf *rssConf = adapter->rss_conf;
unsigned int n = min_t(unsigned int, p->size, rssConf->indTableSize); unsigned int n = rssConf->indTableSize;
p->size = rssConf->indTableSize;
while (n--) while (n--)
p->ring_index[n] = rssConf->indTable[n]; p[n] = rssConf->indTable[n];
return 0; return 0;
} }
static int static int
vmxnet3_set_rss_indir(struct net_device *netdev, vmxnet3_set_rss_indir(struct net_device *netdev, const u32 *p)
const struct ethtool_rxfh_indir *p)
{ {
unsigned int i; unsigned int i;
unsigned long flags; unsigned long flags;
struct vmxnet3_adapter *adapter = netdev_priv(netdev); struct vmxnet3_adapter *adapter = netdev_priv(netdev);
struct UPT1_RSSConf *rssConf = adapter->rss_conf; struct UPT1_RSSConf *rssConf = adapter->rss_conf;
if (p->size != rssConf->indTableSize)
return -EINVAL;
for (i = 0; i < rssConf->indTableSize; i++) {
/*
* Return with error code if any of the queue indices
* is out of range
*/
if (p->ring_index[i] < 0 ||
p->ring_index[i] >= adapter->num_rx_queues)
return -EINVAL;
}
for (i = 0; i < rssConf->indTableSize; i++) for (i = 0; i < rssConf->indTableSize; i++)
rssConf->indTable[i] = p->ring_index[i]; rssConf->indTable[i] = p[i];
spin_lock_irqsave(&adapter->cmd_lock, flags); spin_lock_irqsave(&adapter->cmd_lock, flags);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
...@@ -629,6 +623,7 @@ static struct ethtool_ops vmxnet3_ethtool_ops = { ...@@ -629,6 +623,7 @@ static struct ethtool_ops vmxnet3_ethtool_ops = {
.set_ringparam = vmxnet3_set_ringparam, .set_ringparam = vmxnet3_set_ringparam,
.get_rxnfc = vmxnet3_get_rxnfc, .get_rxnfc = vmxnet3_get_rxnfc,
#ifdef VMXNET3_RSS #ifdef VMXNET3_RSS
.get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
.get_rxfh_indir = vmxnet3_get_rss_indir, .get_rxfh_indir = vmxnet3_get_rss_indir,
.set_rxfh_indir = vmxnet3_set_rss_indir, .set_rxfh_indir = vmxnet3_set_rss_indir,
#endif #endif
......
...@@ -828,9 +828,13 @@ u32 ethtool_op_get_link(struct net_device *dev); ...@@ -828,9 +828,13 @@ u32 ethtool_op_get_link(struct net_device *dev);
* error code or zero. * error code or zero.
* @set_rx_ntuple: Set an RX n-tuple rule. Returns a negative error code * @set_rx_ntuple: Set an RX n-tuple rule. Returns a negative error code
* or zero. * or zero.
* @get_rxfh_indir_size: Get the size of the RX flow hash indirection table.
* Returns zero if not supported for this specific device.
* @get_rxfh_indir: Get the contents of the RX flow hash indirection table. * @get_rxfh_indir: Get the contents of the RX flow hash indirection table.
* Will not be called if @get_rxfh_indir_size returns zero.
* Returns a negative error code or zero. * Returns a negative error code or zero.
* @set_rxfh_indir: Set the contents of the RX flow hash indirection table. * @set_rxfh_indir: Set the contents of the RX flow hash indirection table.
* Will not be called if @get_rxfh_indir_size returns zero.
* Returns a negative error code or zero. * Returns a negative error code or zero.
* @get_channels: Get number of channels. * @get_channels: Get number of channels.
* @set_channels: Set number of channels. Returns a negative error code or * @set_channels: Set number of channels. Returns a negative error code or
...@@ -894,10 +898,9 @@ struct ethtool_ops { ...@@ -894,10 +898,9 @@ struct ethtool_ops {
int (*reset)(struct net_device *, u32 *); int (*reset)(struct net_device *, u32 *);
int (*set_rx_ntuple)(struct net_device *, int (*set_rx_ntuple)(struct net_device *,
struct ethtool_rx_ntuple *); struct ethtool_rx_ntuple *);
int (*get_rxfh_indir)(struct net_device *, u32 (*get_rxfh_indir_size)(struct net_device *);
struct ethtool_rxfh_indir *); int (*get_rxfh_indir)(struct net_device *, u32 *);
int (*set_rxfh_indir)(struct net_device *, int (*set_rxfh_indir)(struct net_device *, const u32 *);
const struct ethtool_rxfh_indir *);
void (*get_channels)(struct net_device *, struct ethtool_channels *); void (*get_channels)(struct net_device *, struct ethtool_channels *);
int (*set_channels)(struct net_device *, struct ethtool_channels *); int (*set_channels)(struct net_device *, struct ethtool_channels *);
int (*get_dump_flag)(struct net_device *, struct ethtool_dump *); int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
......
...@@ -515,34 +515,44 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, ...@@ -515,34 +515,44 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
void __user *useraddr) void __user *useraddr)
{ {
struct ethtool_rxfh_indir *indir; u32 user_size, dev_size;
u32 table_size; u32 *indir;
size_t full_size;
int ret; int ret;
if (!dev->ethtool_ops->get_rxfh_indir) if (!dev->ethtool_ops->get_rxfh_indir_size ||
!dev->ethtool_ops->get_rxfh_indir)
return -EOPNOTSUPP;
dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
if (dev_size == 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (copy_from_user(&table_size, if (copy_from_user(&user_size,
useraddr + offsetof(struct ethtool_rxfh_indir, size), useraddr + offsetof(struct ethtool_rxfh_indir, size),
sizeof(table_size))) sizeof(user_size)))
return -EFAULT; return -EFAULT;
if (table_size > if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size),
(KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index)) &dev_size, sizeof(dev_size)))
return -ENOMEM; return -EFAULT;
full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size;
indir = kzalloc(full_size, GFP_USER); /* If the user buffer size is 0, this is just a query for the
* device table size. Otherwise, if it's smaller than the
* device table size it's an error.
*/
if (user_size < dev_size)
return user_size == 0 ? 0 : -EINVAL;
indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
if (!indir) if (!indir)
return -ENOMEM; return -ENOMEM;
indir->cmd = ETHTOOL_GRXFHINDIR;
indir->size = table_size;
ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
if (ret) if (ret)
goto out; goto out;
if (copy_to_user(useraddr, indir, full_size)) if (copy_to_user(useraddr +
offsetof(struct ethtool_rxfh_indir, ring_index[0]),
indir, dev_size * sizeof(indir[0])))
ret = -EFAULT; ret = -EFAULT;
out: out:
...@@ -553,32 +563,51 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, ...@@ -553,32 +563,51 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
void __user *useraddr) void __user *useraddr)
{ {
struct ethtool_rxfh_indir *indir; struct ethtool_rxnfc rx_rings;
u32 table_size; u32 user_size, dev_size, i;
size_t full_size; u32 *indir;
int ret; int ret;
if (!dev->ethtool_ops->set_rxfh_indir) if (!dev->ethtool_ops->get_rxfh_indir_size ||
!dev->ethtool_ops->set_rxfh_indir ||
!dev->ethtool_ops->get_rxnfc)
return -EOPNOTSUPP;
dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
if (dev_size == 0)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (copy_from_user(&table_size, if (copy_from_user(&user_size,
useraddr + offsetof(struct ethtool_rxfh_indir, size), useraddr + offsetof(struct ethtool_rxfh_indir, size),
sizeof(table_size))) sizeof(user_size)))
return -EFAULT; return -EFAULT;
if (table_size > if (user_size != dev_size)
(KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index)) return -EINVAL;
return -ENOMEM;
full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size; indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
indir = kmalloc(full_size, GFP_USER);
if (!indir) if (!indir)
return -ENOMEM; return -ENOMEM;
if (copy_from_user(indir, useraddr, full_size)) { if (copy_from_user(indir,
useraddr +
offsetof(struct ethtool_rxfh_indir, ring_index[0]),
dev_size * sizeof(indir[0]))) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
/* Validate ring indices */
rx_rings.cmd = ETHTOOL_GRXRINGS;
ret = dev->ethtool_ops->get_rxnfc(dev, &rx_rings, NULL);
if (ret)
goto out;
for (i = 0; i < dev_size; i++) {
if (indir[i] >= rx_rings.data) {
ret = -EINVAL;
goto out;
}
}
ret = dev->ethtool_ops->set_rxfh_indir(dev, indir); ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment