aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-12-15 08:55:01 -0500
committerDavid S. Miller <davem@davemloft.net>2011-12-16 13:52:47 -0500
commit7850f63f1620512631445b901ae11cd149e7375c (patch)
treed26a5f049dcf3634c4bf9e1b86915d201fab3836
parent14596f7006297b67516e2b6a2b26bcb11fe08fb3 (diff)
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: Ben Hutchings <bhutchings@solarflare.com> Acked-by: Dimitris Michailidis <dm@chelsio.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c39
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c27
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c35
-rw-r--r--drivers/net/vmxnet3/vmxnet3_ethtool.c35
-rw-r--r--include/linux/ethtool.h11
-rw-r--r--net/core/ethtool.c81
6 files changed, 117 insertions, 111 deletions
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index 90d44af85600..a688b9d975a2 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -2302,18 +2302,20 @@ static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
2302 } 2302 }
2303} 2303}
2304 2304
2305static int bnx2x_get_rxfh_indir(struct net_device *dev, 2305static u32 bnx2x_get_rxfh_indir_size(struct net_device *dev)
2306 struct ethtool_rxfh_indir *indir) 2306{
2307 struct bnx2x *bp = netdev_priv(dev);
2308
2309 return (bp->multi_mode == ETH_RSS_MODE_DISABLED ?
2310 0 : T_ETH_INDIRECTION_TABLE_SIZE);
2311}
2312
2313static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
2307{ 2314{
2308 struct bnx2x *bp = netdev_priv(dev); 2315 struct bnx2x *bp = netdev_priv(dev);
2309 size_t copy_size =
2310 min_t(size_t, indir->size, T_ETH_INDIRECTION_TABLE_SIZE);
2311 u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; 2316 u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
2312 size_t i; 2317 size_t i;
2313 2318
2314 if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
2315 return -EOPNOTSUPP;
2316
2317 /* Get the current configuration of the RSS indirection table */ 2319 /* Get the current configuration of the RSS indirection table */
2318 bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table); 2320 bnx2x_get_rss_ind_table(&bp->rss_conf_obj, ind_table);
2319 2321
@@ -2326,33 +2328,19 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev,
2326 * align the returned table to the Client ID of the leading RSS 2328 * align the returned table to the Client ID of the leading RSS
2327 * queue. 2329 * queue.
2328 */ 2330 */
2329 for (i = 0; i < copy_size; i++) 2331 for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++)
2330 indir->ring_index[i] = ind_table[i] - bp->fp->cl_id; 2332 indir[i] = ind_table[i] - bp->fp->cl_id;
2331
2332 indir->size = T_ETH_INDIRECTION_TABLE_SIZE;
2333 2333
2334 return 0; 2334 return 0;
2335} 2335}
2336 2336
2337static int bnx2x_set_rxfh_indir(struct net_device *dev, 2337static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir)
2338 const struct ethtool_rxfh_indir *indir)
2339{ 2338{
2340 struct bnx2x *bp = netdev_priv(dev); 2339 struct bnx2x *bp = netdev_priv(dev);
2341 size_t i; 2340 size_t i;
2342 u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; 2341 u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
2343 u32 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp);
2344
2345 if (bp->multi_mode == ETH_RSS_MODE_DISABLED)
2346 return -EOPNOTSUPP;
2347
2348 /* validate the size */
2349 if (indir->size != T_ETH_INDIRECTION_TABLE_SIZE)
2350 return -EINVAL;
2351 2342
2352 for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) { 2343 for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
2353 /* validate the indices */
2354 if (indir->ring_index[i] >= num_eth_queues)
2355 return -EINVAL;
2356 /* 2344 /*
2357 * The same as in bnx2x_get_rxfh_indir: we can't use a memcpy() 2345 * The same as in bnx2x_get_rxfh_indir: we can't use a memcpy()
2358 * as an internal storage of an indirection table is a u8 array 2346 * 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 * align the received table to the Client ID of the leading RSS 2350 * align the received table to the Client ID of the leading RSS
2363 * queue 2351 * queue
2364 */ 2352 */
2365 ind_table[i] = indir->ring_index[i] + bp->fp->cl_id; 2353 ind_table[i] = indir[i] + bp->fp->cl_id;
2366 } 2354 }
2367 2355
2368 return bnx2x_config_rss_pf(bp, ind_table, false); 2356 return bnx2x_config_rss_pf(bp, ind_table, false);
@@ -2395,6 +2383,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
2395 .set_phys_id = bnx2x_set_phys_id, 2383 .set_phys_id = bnx2x_set_phys_id,
2396 .get_ethtool_stats = bnx2x_get_ethtool_stats, 2384 .get_ethtool_stats = bnx2x_get_ethtool_stats,
2397 .get_rxnfc = bnx2x_get_rxnfc, 2385 .get_rxnfc = bnx2x_get_rxnfc,
2386 .get_rxfh_indir_size = bnx2x_get_rxfh_indir_size,
2398 .get_rxfh_indir = bnx2x_get_rxfh_indir, 2387 .get_rxfh_indir = bnx2x_get_rxfh_indir,
2399 .set_rxfh_indir = bnx2x_set_rxfh_indir, 2388 .set_rxfh_indir = bnx2x_set_rxfh_indir,
2400}; 2389};
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index a34e7ce7e214..8ffd55bdef3d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -1871,30 +1871,30 @@ static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
1871 return err; 1871 return err;
1872} 1872}
1873 1873
1874static int get_rss_table(struct net_device *dev, struct ethtool_rxfh_indir *p) 1874static u32 get_rss_table_size(struct net_device *dev)
1875{ 1875{
1876 const struct port_info *pi = netdev_priv(dev); 1876 const struct port_info *pi = netdev_priv(dev);
1877 unsigned int n = min_t(unsigned int, p->size, pi->rss_size);
1878 1877
1879 p->size = pi->rss_size; 1878 return pi->rss_size;
1879}
1880
1881static int get_rss_table(struct net_device *dev, u32 *p)
1882{
1883 const struct port_info *pi = netdev_priv(dev);
1884 unsigned int n = pi->rss_size;
1885
1880 while (n--) 1886 while (n--)
1881 p->ring_index[n] = pi->rss[n]; 1887 p[n] = pi->rss[n];
1882 return 0; 1888 return 0;
1883} 1889}
1884 1890
1885static int set_rss_table(struct net_device *dev, 1891static int set_rss_table(struct net_device *dev, const u32 *p)
1886 const struct ethtool_rxfh_indir *p)
1887{ 1892{
1888 unsigned int i; 1893 unsigned int i;
1889 struct port_info *pi = netdev_priv(dev); 1894 struct port_info *pi = netdev_priv(dev);
1890 1895
1891 if (p->size != pi->rss_size) 1896 for (i = 0; i < pi->rss_size; i++)
1892 return -EINVAL; 1897 pi->rss[i] = p[i];
1893 for (i = 0; i < p->size; i++)
1894 if (p->ring_index[i] >= pi->nqsets)
1895 return -EINVAL;
1896 for (i = 0; i < p->size; i++)
1897 pi->rss[i] = p->ring_index[i];
1898 if (pi->adapter->flags & FULL_INIT_DONE) 1898 if (pi->adapter->flags & FULL_INIT_DONE)
1899 return write_rss(pi, pi->rss); 1899 return write_rss(pi, pi->rss);
1900 return 0; 1900 return 0;
@@ -1989,6 +1989,7 @@ static struct ethtool_ops cxgb_ethtool_ops = {
1989 .get_wol = get_wol, 1989 .get_wol = get_wol,
1990 .set_wol = set_wol, 1990 .set_wol = set_wol,
1991 .get_rxnfc = get_rxnfc, 1991 .get_rxnfc = get_rxnfc,
1992 .get_rxfh_indir_size = get_rss_table_size,
1992 .get_rxfh_indir = get_rss_table, 1993 .get_rxfh_indir = get_rss_table,
1993 .set_rxfh_indir = set_rss_table, 1994 .set_rxfh_indir = set_rss_table,
1994 .flash_device = set_flash, 1995 .flash_device = set_flash,
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index f3cd96dfa398..1be51b2bfa42 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -956,40 +956,28 @@ static int efx_ethtool_set_rx_ntuple(struct net_device *net_dev,
956 return rc < 0 ? rc : 0; 956 return rc < 0 ? rc : 0;
957} 957}
958 958
959static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, 959static u32 efx_ethtool_get_rxfh_indir_size(struct net_device *net_dev)
960 struct ethtool_rxfh_indir *indir)
961{ 960{
962 struct efx_nic *efx = netdev_priv(net_dev); 961 struct efx_nic *efx = netdev_priv(net_dev);
963 size_t copy_size =
964 min_t(size_t, indir->size, ARRAY_SIZE(efx->rx_indir_table));
965 962
966 if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) 963 return (efx_nic_rev(efx) < EFX_REV_FALCON_B0 ?
967 return -EOPNOTSUPP; 964 0 : ARRAY_SIZE(efx->rx_indir_table));
965}
966
967static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, u32 *indir)
968{
969 struct efx_nic *efx = netdev_priv(net_dev);
968 970
969 indir->size = ARRAY_SIZE(efx->rx_indir_table); 971 memcpy(indir, efx->rx_indir_table, sizeof(efx->rx_indir_table));
970 memcpy(indir->ring_index, efx->rx_indir_table,
971 copy_size * sizeof(indir->ring_index[0]));
972 return 0; 972 return 0;
973} 973}
974 974
975static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, 975static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
976 const struct ethtool_rxfh_indir *indir) 976 const u32 *indir)
977{ 977{
978 struct efx_nic *efx = netdev_priv(net_dev); 978 struct efx_nic *efx = netdev_priv(net_dev);
979 size_t i;
980
981 if (efx_nic_rev(efx) < EFX_REV_FALCON_B0)
982 return -EOPNOTSUPP;
983
984 /* Validate size and indices */
985 if (indir->size != ARRAY_SIZE(efx->rx_indir_table))
986 return -EINVAL;
987 for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++)
988 if (indir->ring_index[i] >= efx->n_rx_channels)
989 return -EINVAL;
990 979
991 memcpy(efx->rx_indir_table, indir->ring_index, 980 memcpy(efx->rx_indir_table, indir, sizeof(efx->rx_indir_table));
992 sizeof(efx->rx_indir_table));
993 efx_nic_push_rx_indir_table(efx); 981 efx_nic_push_rx_indir_table(efx);
994 return 0; 982 return 0;
995} 983}
@@ -1020,6 +1008,7 @@ const struct ethtool_ops efx_ethtool_ops = {
1020 .reset = efx_ethtool_reset, 1008 .reset = efx_ethtool_reset,
1021 .get_rxnfc = efx_ethtool_get_rxnfc, 1009 .get_rxnfc = efx_ethtool_get_rxnfc,
1022 .set_rx_ntuple = efx_ethtool_set_rx_ntuple, 1010 .set_rx_ntuple = efx_ethtool_set_rx_ntuple,
1011 .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size,
1023 .get_rxfh_indir = efx_ethtool_get_rxfh_indir, 1012 .get_rxfh_indir = efx_ethtool_get_rxfh_indir,
1024 .set_rxfh_indir = efx_ethtool_set_rxfh_indir, 1013 .set_rxfh_indir = efx_ethtool_set_rxfh_indir,
1025}; 1014};
diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
index b492ee1e5f17..a3eb75a62ea9 100644
--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c
+++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
@@ -565,44 +565,38 @@ vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
565} 565}
566 566
567#ifdef VMXNET3_RSS 567#ifdef VMXNET3_RSS
568static u32
569vmxnet3_get_rss_indir_size(struct net_device *netdev)
570{
571 struct vmxnet3_adapter *adapter = netdev_priv(netdev);
572 struct UPT1_RSSConf *rssConf = adapter->rss_conf;
573
574 return rssConf->indTableSize;
575}
576
568static int 577static int
569vmxnet3_get_rss_indir(struct net_device *netdev, 578vmxnet3_get_rss_indir(struct net_device *netdev, u32 *p)
570 struct ethtool_rxfh_indir *p)
571{ 579{
572 struct vmxnet3_adapter *adapter = netdev_priv(netdev); 580 struct vmxnet3_adapter *adapter = netdev_priv(netdev);
573 struct UPT1_RSSConf *rssConf = adapter->rss_conf; 581 struct UPT1_RSSConf *rssConf = adapter->rss_conf;
574 unsigned int n = min_t(unsigned int, p->size, rssConf->indTableSize); 582 unsigned int n = rssConf->indTableSize;
575 583
576 p->size = rssConf->indTableSize;
577 while (n--) 584 while (n--)
578 p->ring_index[n] = rssConf->indTable[n]; 585 p[n] = rssConf->indTable[n];
579 return 0; 586 return 0;
580 587
581} 588}
582 589
583static int 590static int
584vmxnet3_set_rss_indir(struct net_device *netdev, 591vmxnet3_set_rss_indir(struct net_device *netdev, const u32 *p)
585 const struct ethtool_rxfh_indir *p)
586{ 592{
587 unsigned int i; 593 unsigned int i;
588 unsigned long flags; 594 unsigned long flags;
589 struct vmxnet3_adapter *adapter = netdev_priv(netdev); 595 struct vmxnet3_adapter *adapter = netdev_priv(netdev);
590 struct UPT1_RSSConf *rssConf = adapter->rss_conf; 596 struct UPT1_RSSConf *rssConf = adapter->rss_conf;
591 597
592 if (p->size != rssConf->indTableSize)
593 return -EINVAL;
594 for (i = 0; i < rssConf->indTableSize; i++) {
595 /*
596 * Return with error code if any of the queue indices
597 * is out of range
598 */
599 if (p->ring_index[i] < 0 ||
600 p->ring_index[i] >= adapter->num_rx_queues)
601 return -EINVAL;
602 }
603
604 for (i = 0; i < rssConf->indTableSize; i++) 598 for (i = 0; i < rssConf->indTableSize; i++)
605 rssConf->indTable[i] = p->ring_index[i]; 599 rssConf->indTable[i] = p[i];
606 600
607 spin_lock_irqsave(&adapter->cmd_lock, flags); 601 spin_lock_irqsave(&adapter->cmd_lock, flags);
608 VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, 602 VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
@@ -629,6 +623,7 @@ static struct ethtool_ops vmxnet3_ethtool_ops = {
629 .set_ringparam = vmxnet3_set_ringparam, 623 .set_ringparam = vmxnet3_set_ringparam,
630 .get_rxnfc = vmxnet3_get_rxnfc, 624 .get_rxnfc = vmxnet3_get_rxnfc,
631#ifdef VMXNET3_RSS 625#ifdef VMXNET3_RSS
626 .get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
632 .get_rxfh_indir = vmxnet3_get_rss_indir, 627 .get_rxfh_indir = vmxnet3_get_rss_indir,
633 .set_rxfh_indir = vmxnet3_set_rss_indir, 628 .set_rxfh_indir = vmxnet3_set_rss_indir,
634#endif 629#endif
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 0ec2fd412d03..3b9f09d55b5c 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -828,9 +828,13 @@ u32 ethtool_op_get_link(struct net_device *dev);
828 * error code or zero. 828 * error code or zero.
829 * @set_rx_ntuple: Set an RX n-tuple rule. Returns a negative error code 829 * @set_rx_ntuple: Set an RX n-tuple rule. Returns a negative error code
830 * or zero. 830 * or zero.
831 * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table.
832 * Returns zero if not supported for this specific device.
831 * @get_rxfh_indir: Get the contents of the RX flow hash indirection table. 833 * @get_rxfh_indir: Get the contents of the RX flow hash indirection table.
834 * Will not be called if @get_rxfh_indir_size returns zero.
832 * Returns a negative error code or zero. 835 * Returns a negative error code or zero.
833 * @set_rxfh_indir: Set the contents of the RX flow hash indirection table. 836 * @set_rxfh_indir: Set the contents of the RX flow hash indirection table.
837 * Will not be called if @get_rxfh_indir_size returns zero.
834 * Returns a negative error code or zero. 838 * Returns a negative error code or zero.
835 * @get_channels: Get number of channels. 839 * @get_channels: Get number of channels.
836 * @set_channels: Set number of channels. Returns a negative error code or 840 * @set_channels: Set number of channels. Returns a negative error code or
@@ -894,10 +898,9 @@ struct ethtool_ops {
894 int (*reset)(struct net_device *, u32 *); 898 int (*reset)(struct net_device *, u32 *);
895 int (*set_rx_ntuple)(struct net_device *, 899 int (*set_rx_ntuple)(struct net_device *,
896 struct ethtool_rx_ntuple *); 900 struct ethtool_rx_ntuple *);
897 int (*get_rxfh_indir)(struct net_device *, 901 u32 (*get_rxfh_indir_size)(struct net_device *);
898 struct ethtool_rxfh_indir *); 902 int (*get_rxfh_indir)(struct net_device *, u32 *);
899 int (*set_rxfh_indir)(struct net_device *, 903 int (*set_rxfh_indir)(struct net_device *, const u32 *);
900 const struct ethtool_rxfh_indir *);
901 void (*get_channels)(struct net_device *, struct ethtool_channels *); 904 void (*get_channels)(struct net_device *, struct ethtool_channels *);
902 int (*set_channels)(struct net_device *, struct ethtool_channels *); 905 int (*set_channels)(struct net_device *, struct ethtool_channels *);
903 int (*get_dump_flag)(struct net_device *, struct ethtool_dump *); 906 int (*get_dump_flag)(struct net_device *, struct ethtool_dump *);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 31b0b7f5383e..69f71b86b035 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -515,34 +515,44 @@ err_out:
515static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, 515static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
516 void __user *useraddr) 516 void __user *useraddr)
517{ 517{
518 struct ethtool_rxfh_indir *indir; 518 u32 user_size, dev_size;
519 u32 table_size; 519 u32 *indir;
520 size_t full_size;
521 int ret; 520 int ret;
522 521
523 if (!dev->ethtool_ops->get_rxfh_indir) 522 if (!dev->ethtool_ops->get_rxfh_indir_size ||
523 !dev->ethtool_ops->get_rxfh_indir)
524 return -EOPNOTSUPP;
525 dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
526 if (dev_size == 0)
524 return -EOPNOTSUPP; 527 return -EOPNOTSUPP;
525 528
526 if (copy_from_user(&table_size, 529 if (copy_from_user(&user_size,
527 useraddr + offsetof(struct ethtool_rxfh_indir, size), 530 useraddr + offsetof(struct ethtool_rxfh_indir, size),
528 sizeof(table_size))) 531 sizeof(user_size)))
529 return -EFAULT; 532 return -EFAULT;
530 533
531 if (table_size > 534 if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size),
532 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index)) 535 &dev_size, sizeof(dev_size)))
533 return -ENOMEM; 536 return -EFAULT;
534 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size; 537
535 indir = kzalloc(full_size, GFP_USER); 538 /* If the user buffer size is 0, this is just a query for the
539 * device table size. Otherwise, if it's smaller than the
540 * device table size it's an error.
541 */
542 if (user_size < dev_size)
543 return user_size == 0 ? 0 : -EINVAL;
544
545 indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
536 if (!indir) 546 if (!indir)
537 return -ENOMEM; 547 return -ENOMEM;
538 548
539 indir->cmd = ETHTOOL_GRXFHINDIR;
540 indir->size = table_size;
541 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); 549 ret = dev->ethtool_ops->get_rxfh_indir(dev, indir);
542 if (ret) 550 if (ret)
543 goto out; 551 goto out;
544 552
545 if (copy_to_user(useraddr, indir, full_size)) 553 if (copy_to_user(useraddr +
554 offsetof(struct ethtool_rxfh_indir, ring_index[0]),
555 indir, dev_size * sizeof(indir[0])))
546 ret = -EFAULT; 556 ret = -EFAULT;
547 557
548out: 558out:
@@ -553,32 +563,51 @@ out:
553static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, 563static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
554 void __user *useraddr) 564 void __user *useraddr)
555{ 565{
556 struct ethtool_rxfh_indir *indir; 566 struct ethtool_rxnfc rx_rings;
557 u32 table_size; 567 u32 user_size, dev_size, i;
558 size_t full_size; 568 u32 *indir;
559 int ret; 569 int ret;
560 570
561 if (!dev->ethtool_ops->set_rxfh_indir) 571 if (!dev->ethtool_ops->get_rxfh_indir_size ||
572 !dev->ethtool_ops->set_rxfh_indir ||
573 !dev->ethtool_ops->get_rxnfc)
574 return -EOPNOTSUPP;
575 dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev);
576 if (dev_size == 0)
562 return -EOPNOTSUPP; 577 return -EOPNOTSUPP;
563 578
564 if (copy_from_user(&table_size, 579 if (copy_from_user(&user_size,
565 useraddr + offsetof(struct ethtool_rxfh_indir, size), 580 useraddr + offsetof(struct ethtool_rxfh_indir, size),
566 sizeof(table_size))) 581 sizeof(user_size)))
567 return -EFAULT; 582 return -EFAULT;
568 583
569 if (table_size > 584 if (user_size != dev_size)
570 (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index)) 585 return -EINVAL;
571 return -ENOMEM; 586
572 full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size; 587 indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER);
573 indir = kmalloc(full_size, GFP_USER);
574 if (!indir) 588 if (!indir)
575 return -ENOMEM; 589 return -ENOMEM;
576 590
577 if (copy_from_user(indir, useraddr, full_size)) { 591 if (copy_from_user(indir,
592 useraddr +
593 offsetof(struct ethtool_rxfh_indir, ring_index[0]),
594 dev_size * sizeof(indir[0]))) {
578 ret = -EFAULT; 595 ret = -EFAULT;
579 goto out; 596 goto out;
580 } 597 }
581 598
599 /* Validate ring indices */
600 rx_rings.cmd = ETHTOOL_GRXRINGS;
601 ret = dev->ethtool_ops->get_rxnfc(dev, &rx_rings, NULL);
602 if (ret)
603 goto out;
604 for (i = 0; i < dev_size; i++) {
605 if (indir[i] >= rx_rings.data) {
606 ret = -EINVAL;
607 goto out;
608 }
609 }
610
582 ret = dev->ethtool_ops->set_rxfh_indir(dev, indir); 611 ret = dev->ethtool_ops->set_rxfh_indir(dev, indir);
583 612
584out: 613out: