diff options
author | David S. Miller <davem@davemloft.net> | 2014-06-03 02:07:02 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-06-03 02:07:02 -0400 |
commit | 014b20133bcd442db554c2d2d86181b34cd15b66 (patch) | |
tree | 3402034d4bba5628d21c98258634c58d5bb46bb7 /net | |
parent | a68ab98e6c7ab0955babcdc45ca446886f3bfb25 (diff) | |
parent | f062a3844845d267e3716cbc188ad502a15898b7 (diff) |
Merge branch 'ethtool-rssh-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/net-next
Ben Hutchings says:
====================
Pull request: Fixes for new ethtool RSS commands
This addresses several problems I previously identified with the new
ETHTOOL_{G,S}RSSH commands:
1. Missing validation of reserved parameters
2. Vague documentation
3. Use of unnamed magic number
4. No consolidation with existing driver operations
I don't currently have access to suitable network hardware, but have
tested these changes with a dummy driver that can support various
combinations of operations and sizes, together with (a) Debian's ethtool
3.13 (b) ethtool 3.14 with the submitted patch to use ETHTOOL_{G,S}RSSH
and minor adjustment for fixes 1 and 3.
v2: Update RSS operations in vmxnet3 too
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/ethtool.c | 110 |
1 files changed, 48 insertions, 62 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index aa8978ac47d2..17cb912793fa 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -561,19 +561,17 @@ static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr, | |||
561 | struct ethtool_rxnfc *rx_rings, | 561 | struct ethtool_rxnfc *rx_rings, |
562 | u32 size) | 562 | u32 size) |
563 | { | 563 | { |
564 | int ret = 0, i; | 564 | int i; |
565 | 565 | ||
566 | if (copy_from_user(indir, useraddr, size * sizeof(indir[0]))) | 566 | if (copy_from_user(indir, useraddr, size * sizeof(indir[0]))) |
567 | ret = -EFAULT; | 567 | return -EFAULT; |
568 | 568 | ||
569 | /* Validate ring indices */ | 569 | /* Validate ring indices */ |
570 | for (i = 0; i < size; i++) { | 570 | for (i = 0; i < size; i++) |
571 | if (indir[i] >= rx_rings->data) { | 571 | if (indir[i] >= rx_rings->data) |
572 | ret = -EINVAL; | 572 | return -EINVAL; |
573 | break; | 573 | |
574 | } | 574 | return 0; |
575 | } | ||
576 | return ret; | ||
577 | } | 575 | } |
578 | 576 | ||
579 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, | 577 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, |
@@ -584,7 +582,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, | |||
584 | int ret; | 582 | int ret; |
585 | 583 | ||
586 | if (!dev->ethtool_ops->get_rxfh_indir_size || | 584 | if (!dev->ethtool_ops->get_rxfh_indir_size || |
587 | !dev->ethtool_ops->get_rxfh_indir) | 585 | !dev->ethtool_ops->get_rxfh) |
588 | return -EOPNOTSUPP; | 586 | return -EOPNOTSUPP; |
589 | dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); | 587 | dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); |
590 | if (dev_size == 0) | 588 | if (dev_size == 0) |
@@ -610,7 +608,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, | |||
610 | if (!indir) | 608 | if (!indir) |
611 | return -ENOMEM; | 609 | return -ENOMEM; |
612 | 610 | ||
613 | ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); | 611 | ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL); |
614 | if (ret) | 612 | if (ret) |
615 | goto out; | 613 | goto out; |
616 | 614 | ||
@@ -634,7 +632,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, | |||
634 | int ret; | 632 | int ret; |
635 | u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]); | 633 | u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]); |
636 | 634 | ||
637 | if (!ops->get_rxfh_indir_size || !ops->set_rxfh_indir || | 635 | if (!ops->get_rxfh_indir_size || !ops->set_rxfh || |
638 | !ops->get_rxnfc) | 636 | !ops->get_rxnfc) |
639 | return -EOPNOTSUPP; | 637 | return -EOPNOTSUPP; |
640 | 638 | ||
@@ -671,7 +669,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, | |||
671 | goto out; | 669 | goto out; |
672 | } | 670 | } |
673 | 671 | ||
674 | ret = ops->set_rxfh_indir(dev, indir); | 672 | ret = ops->set_rxfh(dev, indir, NULL); |
675 | 673 | ||
676 | out: | 674 | out: |
677 | kfree(indir); | 675 | kfree(indir); |
@@ -683,11 +681,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, | |||
683 | { | 681 | { |
684 | int ret; | 682 | int ret; |
685 | const struct ethtool_ops *ops = dev->ethtool_ops; | 683 | const struct ethtool_ops *ops = dev->ethtool_ops; |
686 | u32 user_indir_size = 0, user_key_size = 0; | 684 | u32 user_indir_size, user_key_size; |
687 | u32 dev_indir_size = 0, dev_key_size = 0; | 685 | u32 dev_indir_size = 0, dev_key_size = 0; |
686 | struct ethtool_rxfh rxfh; | ||
688 | u32 total_size; | 687 | u32 total_size; |
689 | u32 indir_offset, indir_bytes; | 688 | u32 indir_bytes; |
690 | u32 key_offset; | ||
691 | u32 *indir = NULL; | 689 | u32 *indir = NULL; |
692 | u8 *hkey = NULL; | 690 | u8 *hkey = NULL; |
693 | u8 *rss_config; | 691 | u8 *rss_config; |
@@ -699,33 +697,24 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, | |||
699 | 697 | ||
700 | if (ops->get_rxfh_indir_size) | 698 | if (ops->get_rxfh_indir_size) |
701 | dev_indir_size = ops->get_rxfh_indir_size(dev); | 699 | dev_indir_size = ops->get_rxfh_indir_size(dev); |
702 | |||
703 | indir_offset = offsetof(struct ethtool_rxfh, indir_size); | ||
704 | |||
705 | if (copy_from_user(&user_indir_size, | ||
706 | useraddr + indir_offset, | ||
707 | sizeof(user_indir_size))) | ||
708 | return -EFAULT; | ||
709 | |||
710 | if (copy_to_user(useraddr + indir_offset, | ||
711 | &dev_indir_size, sizeof(dev_indir_size))) | ||
712 | return -EFAULT; | ||
713 | |||
714 | if (ops->get_rxfh_key_size) | 700 | if (ops->get_rxfh_key_size) |
715 | dev_key_size = ops->get_rxfh_key_size(dev); | 701 | dev_key_size = ops->get_rxfh_key_size(dev); |
716 | 702 | ||
717 | if ((dev_key_size + dev_indir_size) == 0) | 703 | if ((dev_key_size + dev_indir_size) == 0) |
718 | return -EOPNOTSUPP; | 704 | return -EOPNOTSUPP; |
719 | 705 | ||
720 | key_offset = offsetof(struct ethtool_rxfh, key_size); | 706 | if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) |
721 | |||
722 | if (copy_from_user(&user_key_size, | ||
723 | useraddr + key_offset, | ||
724 | sizeof(user_key_size))) | ||
725 | return -EFAULT; | 707 | return -EFAULT; |
708 | user_indir_size = rxfh.indir_size; | ||
709 | user_key_size = rxfh.key_size; | ||
726 | 710 | ||
727 | if (copy_to_user(useraddr + key_offset, | 711 | /* Check that reserved fields are 0 for now */ |
728 | &dev_key_size, sizeof(dev_key_size))) | 712 | if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1]) |
713 | return -EINVAL; | ||
714 | |||
715 | rxfh.indir_size = dev_indir_size; | ||
716 | rxfh.key_size = dev_key_size; | ||
717 | if (copy_to_user(useraddr, &rxfh, sizeof(rxfh))) | ||
729 | return -EFAULT; | 718 | return -EFAULT; |
730 | 719 | ||
731 | /* If the user buffer size is 0, this is just a query for the | 720 | /* If the user buffer size is 0, this is just a query for the |
@@ -770,12 +759,11 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, | |||
770 | int ret; | 759 | int ret; |
771 | const struct ethtool_ops *ops = dev->ethtool_ops; | 760 | const struct ethtool_ops *ops = dev->ethtool_ops; |
772 | struct ethtool_rxnfc rx_rings; | 761 | struct ethtool_rxnfc rx_rings; |
773 | u32 user_indir_size = 0, dev_indir_size = 0, i; | 762 | struct ethtool_rxfh rxfh; |
774 | u32 user_key_size = 0, dev_key_size = 0; | 763 | u32 dev_indir_size = 0, dev_key_size = 0, i; |
775 | u32 *indir = NULL, indir_bytes = 0; | 764 | u32 *indir = NULL, indir_bytes = 0; |
776 | u8 *hkey = NULL; | 765 | u8 *hkey = NULL; |
777 | u8 *rss_config; | 766 | u8 *rss_config; |
778 | u32 indir_offset, key_offset; | ||
779 | u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); | 767 | u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); |
780 | 768 | ||
781 | if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || | 769 | if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || |
@@ -784,36 +772,33 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, | |||
784 | 772 | ||
785 | if (ops->get_rxfh_indir_size) | 773 | if (ops->get_rxfh_indir_size) |
786 | dev_indir_size = ops->get_rxfh_indir_size(dev); | 774 | dev_indir_size = ops->get_rxfh_indir_size(dev); |
787 | |||
788 | indir_offset = offsetof(struct ethtool_rxfh, indir_size); | ||
789 | if (copy_from_user(&user_indir_size, | ||
790 | useraddr + indir_offset, | ||
791 | sizeof(user_indir_size))) | ||
792 | return -EFAULT; | ||
793 | |||
794 | if (ops->get_rxfh_key_size) | 775 | if (ops->get_rxfh_key_size) |
795 | dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); | 776 | dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); |
796 | |||
797 | if ((dev_key_size + dev_indir_size) == 0) | 777 | if ((dev_key_size + dev_indir_size) == 0) |
798 | return -EOPNOTSUPP; | 778 | return -EOPNOTSUPP; |
799 | 779 | ||
800 | key_offset = offsetof(struct ethtool_rxfh, key_size); | 780 | if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) |
801 | if (copy_from_user(&user_key_size, | ||
802 | useraddr + key_offset, | ||
803 | sizeof(user_key_size))) | ||
804 | return -EFAULT; | 781 | return -EFAULT; |
805 | 782 | ||
783 | /* Check that reserved fields are 0 for now */ | ||
784 | if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1]) | ||
785 | return -EINVAL; | ||
786 | |||
806 | /* If either indir or hash key is valid, proceed further. | 787 | /* If either indir or hash key is valid, proceed further. |
788 | * It is not valid to request that both be unchanged. | ||
807 | */ | 789 | */ |
808 | if ((user_indir_size && ((user_indir_size != 0xDEADBEEF) && | 790 | if ((rxfh.indir_size && |
809 | user_indir_size != dev_indir_size)) || | 791 | rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE && |
810 | (user_key_size && (user_key_size != dev_key_size))) | 792 | rxfh.indir_size != dev_indir_size) || |
793 | (rxfh.key_size && (rxfh.key_size != dev_key_size)) || | ||
794 | (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE && | ||
795 | rxfh.key_size == 0)) | ||
811 | return -EINVAL; | 796 | return -EINVAL; |
812 | 797 | ||
813 | if (user_indir_size != 0xDEADBEEF) | 798 | if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) |
814 | indir_bytes = dev_indir_size * sizeof(indir[0]); | 799 | indir_bytes = dev_indir_size * sizeof(indir[0]); |
815 | 800 | ||
816 | rss_config = kzalloc(indir_bytes + user_key_size, GFP_USER); | 801 | rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER); |
817 | if (!rss_config) | 802 | if (!rss_config) |
818 | return -ENOMEM; | 803 | return -ENOMEM; |
819 | 804 | ||
@@ -822,28 +807,29 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, | |||
822 | if (ret) | 807 | if (ret) |
823 | goto out; | 808 | goto out; |
824 | 809 | ||
825 | /* user_indir_size == 0 means reset the indir table to default. | 810 | /* rxfh.indir_size == 0 means reset the indir table to default. |
826 | * user_indir_size == 0xDEADBEEF means indir setting is not requested. | 811 | * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged. |
827 | */ | 812 | */ |
828 | if (user_indir_size && user_indir_size != 0xDEADBEEF) { | 813 | if (rxfh.indir_size && |
814 | rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) { | ||
829 | indir = (u32 *)rss_config; | 815 | indir = (u32 *)rss_config; |
830 | ret = ethtool_copy_validate_indir(indir, | 816 | ret = ethtool_copy_validate_indir(indir, |
831 | useraddr + rss_cfg_offset, | 817 | useraddr + rss_cfg_offset, |
832 | &rx_rings, | 818 | &rx_rings, |
833 | user_indir_size); | 819 | rxfh.indir_size); |
834 | if (ret) | 820 | if (ret) |
835 | goto out; | 821 | goto out; |
836 | } else if (user_indir_size == 0) { | 822 | } else if (rxfh.indir_size == 0) { |
837 | indir = (u32 *)rss_config; | 823 | indir = (u32 *)rss_config; |
838 | for (i = 0; i < dev_indir_size; i++) | 824 | for (i = 0; i < dev_indir_size; i++) |
839 | indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); | 825 | indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); |
840 | } | 826 | } |
841 | 827 | ||
842 | if (user_key_size) { | 828 | if (rxfh.key_size) { |
843 | hkey = rss_config + indir_bytes; | 829 | hkey = rss_config + indir_bytes; |
844 | if (copy_from_user(hkey, | 830 | if (copy_from_user(hkey, |
845 | useraddr + rss_cfg_offset + indir_bytes, | 831 | useraddr + rss_cfg_offset + indir_bytes, |
846 | user_key_size)) { | 832 | rxfh.key_size)) { |
847 | ret = -EFAULT; | 833 | ret = -EFAULT; |
848 | goto out; | 834 | goto out; |
849 | } | 835 | } |