diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2011-12-15 08:55:01 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-12-16 13:52:47 -0500 |
commit | 7850f63f1620512631445b901ae11cd149e7375c (patch) | |
tree | d26a5f049dcf3634c4bf9e1b86915d201fab3836 | |
parent | 14596f7006297b67516e2b6a2b26bcb11fe08fb3 (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.c | 39 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 27 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 35 | ||||
-rw-r--r-- | drivers/net/vmxnet3/vmxnet3_ethtool.c | 35 | ||||
-rw-r--r-- | include/linux/ethtool.h | 11 | ||||
-rw-r--r-- | net/core/ethtool.c | 81 |
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 | ||
2305 | static int bnx2x_get_rxfh_indir(struct net_device *dev, | 2305 | static 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 | |||
2313 | static 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 | ||
2337 | static int bnx2x_set_rxfh_indir(struct net_device *dev, | 2337 | static 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 | ||
1874 | static int get_rss_table(struct net_device *dev, struct ethtool_rxfh_indir *p) | 1874 | static 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 | |||
1881 | static 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 | ||
1885 | static int set_rss_table(struct net_device *dev, | 1891 | static 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 | ||
959 | static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev, | 959 | static 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 | |||
967 | static 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 | ||
975 | static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, | 975 | static 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 |
568 | static u32 | ||
569 | vmxnet3_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 | |||
568 | static int | 577 | static int |
569 | vmxnet3_get_rss_indir(struct net_device *netdev, | 578 | vmxnet3_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 | ||
583 | static int | 590 | static int |
584 | vmxnet3_set_rss_indir(struct net_device *netdev, | 591 | vmxnet3_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: | |||
515 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, | 515 | static 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 | ||
548 | out: | 558 | out: |
@@ -553,32 +563,51 @@ out: | |||
553 | static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, | 563 | static 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 | ||
584 | out: | 613 | out: |