| From foo@baz Fri Nov 2 10:04:04 CET 2018 |
| From: Wenwen Wang <wang6495@umn.edu> |
| Date: Mon, 8 Oct 2018 10:49:35 -0500 |
| Subject: ethtool: fix a privilege escalation bug |
| |
| From: Wenwen Wang <wang6495@umn.edu> |
| |
| [ Upstream commit 58f5bbe331c566f49c9559568f982202a278aa78 ] |
| |
| In dev_ethtool(), the eth command 'ethcmd' is firstly copied from the |
| use-space buffer 'useraddr' and checked to see whether it is |
| ETHTOOL_PERQUEUE. If yes, the sub-command 'sub_cmd' is further copied from |
| the user space. Otherwise, 'sub_cmd' is the same as 'ethcmd'. Next, |
| according to 'sub_cmd', a permission check is enforced through the function |
| ns_capable(). For example, the permission check is required if 'sub_cmd' is |
| ETHTOOL_SCOALESCE, but it is not necessary if 'sub_cmd' is |
| ETHTOOL_GCOALESCE, as suggested in the comment "Allow some commands to be |
| done by anyone". The following execution invokes different handlers |
| according to 'ethcmd'. Specifically, if 'ethcmd' is ETHTOOL_PERQUEUE, |
| ethtool_set_per_queue() is called. In ethtool_set_per_queue(), the kernel |
| object 'per_queue_opt' is copied again from the user-space buffer |
| 'useraddr' and 'per_queue_opt.sub_command' is used to determine which |
| operation should be performed. Given that the buffer 'useraddr' is in the |
| user space, a malicious user can race to change the sub-command between the |
| two copies. In particular, the attacker can supply ETHTOOL_PERQUEUE and |
| ETHTOOL_GCOALESCE to bypass the permission check in dev_ethtool(). Then |
| before ethtool_set_per_queue() is called, the attacker changes |
| ETHTOOL_GCOALESCE to ETHTOOL_SCOALESCE. In this way, the attacker can |
| bypass the permission check and execute ETHTOOL_SCOALESCE. |
| |
| This patch enforces a check in ethtool_set_per_queue() after the second |
| copy from 'useraddr'. If the sub-command is different from the one obtained |
| in the first copy in dev_ethtool(), an error code EINVAL will be returned. |
| |
| Fixes: f38d138a7da6 ("net/ethtool: support set coalesce per queue") |
| Signed-off-by: Wenwen Wang <wang6495@umn.edu> |
| Reviewed-by: Michal Kubecek <mkubecek@suse.cz> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/core/ethtool.c | 8 ++++++-- |
| 1 file changed, 6 insertions(+), 2 deletions(-) |
| |
| --- a/net/core/ethtool.c |
| +++ b/net/core/ethtool.c |
| @@ -2397,13 +2397,17 @@ roll_back: |
| return ret; |
| } |
| |
| -static int ethtool_set_per_queue(struct net_device *dev, void __user *useraddr) |
| +static int ethtool_set_per_queue(struct net_device *dev, |
| + void __user *useraddr, u32 sub_cmd) |
| { |
| struct ethtool_per_queue_op per_queue_opt; |
| |
| if (copy_from_user(&per_queue_opt, useraddr, sizeof(per_queue_opt))) |
| return -EFAULT; |
| |
| + if (per_queue_opt.sub_command != sub_cmd) |
| + return -EINVAL; |
| + |
| switch (per_queue_opt.sub_command) { |
| case ETHTOOL_GCOALESCE: |
| return ethtool_get_per_queue_coalesce(dev, useraddr, &per_queue_opt); |
| @@ -2669,7 +2673,7 @@ int dev_ethtool(struct net *net, struct |
| rc = ethtool_get_phy_stats(dev, useraddr); |
| break; |
| case ETHTOOL_PERQUEUE: |
| - rc = ethtool_set_per_queue(dev, useraddr); |
| + rc = ethtool_set_per_queue(dev, useraddr, sub_cmd); |
| break; |
| case ETHTOOL_GLINKSETTINGS: |
| rc = ethtool_get_link_ksettings(dev, useraddr); |