diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2014-05-15 11:28:07 -0400 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2014-06-02 21:43:16 -0400 |
commit | f062a3844845d267e3716cbc188ad502a15898b7 (patch) | |
tree | 964e372f604b5df8884f941a4f5c152fcfce5fbe /net | |
parent | fe62d001372388abb15a324148c913f9b43722a8 (diff) |
ethtool: Check that reserved fields of struct ethtool_rxfh are 0
We should fail rather than silently ignoring use of these extensions.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/ethtool.c | 89 |
1 files changed, 36 insertions, 53 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 8ae452afb545..17cb912793fa 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -681,11 +681,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, | |||
681 | { | 681 | { |
682 | int ret; | 682 | int ret; |
683 | const struct ethtool_ops *ops = dev->ethtool_ops; | 683 | const struct ethtool_ops *ops = dev->ethtool_ops; |
684 | u32 user_indir_size = 0, user_key_size = 0; | 684 | u32 user_indir_size, user_key_size; |
685 | u32 dev_indir_size = 0, dev_key_size = 0; | 685 | u32 dev_indir_size = 0, dev_key_size = 0; |
686 | struct ethtool_rxfh rxfh; | ||
686 | u32 total_size; | 687 | u32 total_size; |
687 | u32 indir_offset, indir_bytes; | 688 | u32 indir_bytes; |
688 | u32 key_offset; | ||
689 | u32 *indir = NULL; | 689 | u32 *indir = NULL; |
690 | u8 *hkey = NULL; | 690 | u8 *hkey = NULL; |
691 | u8 *rss_config; | 691 | u8 *rss_config; |
@@ -697,33 +697,24 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, | |||
697 | 697 | ||
698 | if (ops->get_rxfh_indir_size) | 698 | if (ops->get_rxfh_indir_size) |
699 | dev_indir_size = ops->get_rxfh_indir_size(dev); | 699 | dev_indir_size = ops->get_rxfh_indir_size(dev); |
700 | |||
701 | indir_offset = offsetof(struct ethtool_rxfh, indir_size); | ||
702 | |||
703 | if (copy_from_user(&user_indir_size, | ||
704 | useraddr + indir_offset, | ||
705 | sizeof(user_indir_size))) | ||
706 | return -EFAULT; | ||
707 | |||
708 | if (copy_to_user(useraddr + indir_offset, | ||
709 | &dev_indir_size, sizeof(dev_indir_size))) | ||
710 | return -EFAULT; | ||
711 | |||
712 | if (ops->get_rxfh_key_size) | 700 | if (ops->get_rxfh_key_size) |
713 | dev_key_size = ops->get_rxfh_key_size(dev); | 701 | dev_key_size = ops->get_rxfh_key_size(dev); |
714 | 702 | ||
715 | if ((dev_key_size + dev_indir_size) == 0) | 703 | if ((dev_key_size + dev_indir_size) == 0) |
716 | return -EOPNOTSUPP; | 704 | return -EOPNOTSUPP; |
717 | 705 | ||
718 | key_offset = offsetof(struct ethtool_rxfh, key_size); | 706 | if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) |
719 | |||
720 | if (copy_from_user(&user_key_size, | ||
721 | useraddr + key_offset, | ||
722 | sizeof(user_key_size))) | ||
723 | return -EFAULT; | 707 | return -EFAULT; |
708 | user_indir_size = rxfh.indir_size; | ||
709 | user_key_size = rxfh.key_size; | ||
724 | 710 | ||
725 | if (copy_to_user(useraddr + key_offset, | 711 | /* Check that reserved fields are 0 for now */ |
726 | &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))) | ||
727 | return -EFAULT; | 718 | return -EFAULT; |
728 | 719 | ||
729 | /* 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 |
@@ -768,12 +759,11 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, | |||
768 | int ret; | 759 | int ret; |
769 | const struct ethtool_ops *ops = dev->ethtool_ops; | 760 | const struct ethtool_ops *ops = dev->ethtool_ops; |
770 | struct ethtool_rxnfc rx_rings; | 761 | struct ethtool_rxnfc rx_rings; |
771 | u32 user_indir_size = 0, dev_indir_size = 0, i; | 762 | struct ethtool_rxfh rxfh; |
772 | u32 user_key_size = 0, dev_key_size = 0; | 763 | u32 dev_indir_size = 0, dev_key_size = 0, i; |
773 | u32 *indir = NULL, indir_bytes = 0; | 764 | u32 *indir = NULL, indir_bytes = 0; |
774 | u8 *hkey = NULL; | 765 | u8 *hkey = NULL; |
775 | u8 *rss_config; | 766 | u8 *rss_config; |
776 | u32 indir_offset, key_offset; | ||
777 | u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); | 767 | u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); |
778 | 768 | ||
779 | if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || | 769 | if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || |
@@ -782,40 +772,33 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, | |||
782 | 772 | ||
783 | if (ops->get_rxfh_indir_size) | 773 | if (ops->get_rxfh_indir_size) |
784 | dev_indir_size = ops->get_rxfh_indir_size(dev); | 774 | dev_indir_size = ops->get_rxfh_indir_size(dev); |
785 | |||
786 | indir_offset = offsetof(struct ethtool_rxfh, indir_size); | ||
787 | if (copy_from_user(&user_indir_size, | ||
788 | useraddr + indir_offset, | ||
789 | sizeof(user_indir_size))) | ||
790 | return -EFAULT; | ||
791 | |||
792 | if (ops->get_rxfh_key_size) | 775 | if (ops->get_rxfh_key_size) |
793 | dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); | 776 | dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); |
794 | |||
795 | if ((dev_key_size + dev_indir_size) == 0) | 777 | if ((dev_key_size + dev_indir_size) == 0) |
796 | return -EOPNOTSUPP; | 778 | return -EOPNOTSUPP; |
797 | 779 | ||
798 | key_offset = offsetof(struct ethtool_rxfh, key_size); | 780 | if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) |
799 | if (copy_from_user(&user_key_size, | ||
800 | useraddr + key_offset, | ||
801 | sizeof(user_key_size))) | ||
802 | return -EFAULT; | 781 | return -EFAULT; |
803 | 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 | |||
804 | /* If either indir or hash key is valid, proceed further. | 787 | /* If either indir or hash key is valid, proceed further. |
805 | * It is not valid to request that both be unchanged. | 788 | * It is not valid to request that both be unchanged. |
806 | */ | 789 | */ |
807 | if ((user_indir_size && | 790 | if ((rxfh.indir_size && |
808 | user_indir_size != ETH_RXFH_INDIR_NO_CHANGE && | 791 | rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE && |
809 | user_indir_size != dev_indir_size) || | 792 | rxfh.indir_size != dev_indir_size) || |
810 | (user_key_size && (user_key_size != dev_key_size)) || | 793 | (rxfh.key_size && (rxfh.key_size != dev_key_size)) || |
811 | (user_indir_size == ETH_RXFH_INDIR_NO_CHANGE && | 794 | (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE && |
812 | user_key_size == 0)) | 795 | rxfh.key_size == 0)) |
813 | return -EINVAL; | 796 | return -EINVAL; |
814 | 797 | ||
815 | if (user_indir_size != ETH_RXFH_INDIR_NO_CHANGE) | 798 | if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) |
816 | indir_bytes = dev_indir_size * sizeof(indir[0]); | 799 | indir_bytes = dev_indir_size * sizeof(indir[0]); |
817 | 800 | ||
818 | rss_config = kzalloc(indir_bytes + user_key_size, GFP_USER); | 801 | rss_config = kzalloc(indir_bytes + rxfh.key_size, GFP_USER); |
819 | if (!rss_config) | 802 | if (!rss_config) |
820 | return -ENOMEM; | 803 | return -ENOMEM; |
821 | 804 | ||
@@ -824,29 +807,29 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, | |||
824 | if (ret) | 807 | if (ret) |
825 | goto out; | 808 | goto out; |
826 | 809 | ||
827 | /* user_indir_size == 0 means reset the indir table to default. | 810 | /* rxfh.indir_size == 0 means reset the indir table to default. |
828 | * user_indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged. | 811 | * rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE means leave it unchanged. |
829 | */ | 812 | */ |
830 | if (user_indir_size && | 813 | if (rxfh.indir_size && |
831 | user_indir_size != ETH_RXFH_INDIR_NO_CHANGE) { | 814 | rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) { |
832 | indir = (u32 *)rss_config; | 815 | indir = (u32 *)rss_config; |
833 | ret = ethtool_copy_validate_indir(indir, | 816 | ret = ethtool_copy_validate_indir(indir, |
834 | useraddr + rss_cfg_offset, | 817 | useraddr + rss_cfg_offset, |
835 | &rx_rings, | 818 | &rx_rings, |
836 | user_indir_size); | 819 | rxfh.indir_size); |
837 | if (ret) | 820 | if (ret) |
838 | goto out; | 821 | goto out; |
839 | } else if (user_indir_size == 0) { | 822 | } else if (rxfh.indir_size == 0) { |
840 | indir = (u32 *)rss_config; | 823 | indir = (u32 *)rss_config; |
841 | for (i = 0; i < dev_indir_size; i++) | 824 | for (i = 0; i < dev_indir_size; i++) |
842 | indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); | 825 | indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); |
843 | } | 826 | } |
844 | 827 | ||
845 | if (user_key_size) { | 828 | if (rxfh.key_size) { |
846 | hkey = rss_config + indir_bytes; | 829 | hkey = rss_config + indir_bytes; |
847 | if (copy_from_user(hkey, | 830 | if (copy_from_user(hkey, |
848 | useraddr + rss_cfg_offset + indir_bytes, | 831 | useraddr + rss_cfg_offset + indir_bytes, |
849 | user_key_size)) { | 832 | rxfh.key_size)) { |
850 | ret = -EFAULT; | 833 | ret = -EFAULT; |
851 | goto out; | 834 | goto out; |
852 | } | 835 | } |