]>
Commit | Line | Data |
---|---|---|
b630125f GKH |
1 | From 1adf5345186a99f08ef026bbb3337205802706a1 Mon Sep 17 00:00:00 2001 |
2 | From: Ben Hutchings <ben@decadent.org.uk> | |
3 | Date: Wed, 28 Jul 2010 23:53:47 +0100 | |
4 | Subject: ethtool: Fix potential user buffer overflow for ETHTOOL_{G, S}RXFH | |
5 | ||
6 | commit bf988435bd5b53529f4408a8efb1f433f6ddfda9 upstream. | |
7 | ||
8 | struct ethtool_rxnfc was originally defined in 2.6.27 for the | |
9 | ETHTOOL_{G,S}RXFH command with only the cmd, flow_type and data | |
10 | fields. It was then extended in 2.6.30 to support various additional | |
11 | commands. These commands should have been defined to use a new | |
12 | structure, but it is too late to change that now. | |
13 | ||
14 | Since user-space may still be using the old structure definition | |
15 | for the ETHTOOL_{G,S}RXFH commands, and since they do not need the | |
16 | additional fields, only copy the originally defined fields to and | |
17 | from user-space. | |
18 | ||
19 | Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> | |
20 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
21 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
22 | --- | |
23 | include/linux/ethtool.h | 2 ++ | |
24 | net/core/ethtool.c | 38 +++++++++++++++++++++++++++++--------- | |
25 | 2 files changed, 31 insertions(+), 9 deletions(-) | |
26 | ||
27 | --- a/include/linux/ethtool.h | |
28 | +++ b/include/linux/ethtool.h | |
29 | @@ -357,6 +357,8 @@ struct ethtool_rxnfc { | |
30 | __u32 flow_type; | |
31 | /* The rx flow hash value or the rule DB size */ | |
32 | __u64 data; | |
33 | + /* The following fields are not valid and must not be used for | |
34 | + * the ETHTOOL_{G,X}RXFH commands. */ | |
35 | struct ethtool_rx_flow_spec fs; | |
36 | __u32 rule_cnt; | |
37 | __u32 rule_locs[0]; | |
38 | --- a/net/core/ethtool.c | |
39 | +++ b/net/core/ethtool.c | |
40 | @@ -216,22 +216,34 @@ static int ethtool_get_drvinfo(struct ne | |
41 | return 0; | |
42 | } | |
43 | ||
44 | -static int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr) | |
45 | +static int ethtool_set_rxnfc(struct net_device *dev, | |
46 | + u32 cmd, void __user *useraddr) | |
47 | { | |
48 | - struct ethtool_rxnfc cmd; | |
49 | + struct ethtool_rxnfc info; | |
50 | + size_t info_size = sizeof(info); | |
51 | ||
52 | if (!dev->ethtool_ops->set_rxnfc) | |
53 | return -EOPNOTSUPP; | |
54 | ||
55 | - if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | |
56 | + /* struct ethtool_rxnfc was originally defined for | |
57 | + * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data | |
58 | + * members. User-space might still be using that | |
59 | + * definition. */ | |
60 | + if (cmd == ETHTOOL_SRXFH) | |
61 | + info_size = (offsetof(struct ethtool_rxnfc, data) + | |
62 | + sizeof(info.data)); | |
63 | + | |
64 | + if (copy_from_user(&info, useraddr, info_size)) | |
65 | return -EFAULT; | |
66 | ||
67 | - return dev->ethtool_ops->set_rxnfc(dev, &cmd); | |
68 | + return dev->ethtool_ops->set_rxnfc(dev, &info); | |
69 | } | |
70 | ||
71 | -static int ethtool_get_rxnfc(struct net_device *dev, void __user *useraddr) | |
72 | +static int ethtool_get_rxnfc(struct net_device *dev, | |
73 | + u32 cmd, void __user *useraddr) | |
74 | { | |
75 | struct ethtool_rxnfc info; | |
76 | + size_t info_size = sizeof(info); | |
77 | const struct ethtool_ops *ops = dev->ethtool_ops; | |
78 | int ret; | |
79 | void *rule_buf = NULL; | |
80 | @@ -239,7 +251,15 @@ static int ethtool_get_rxnfc(struct net_ | |
81 | if (!ops->get_rxnfc) | |
82 | return -EOPNOTSUPP; | |
83 | ||
84 | - if (copy_from_user(&info, useraddr, sizeof(info))) | |
85 | + /* struct ethtool_rxnfc was originally defined for | |
86 | + * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data | |
87 | + * members. User-space might still be using that | |
88 | + * definition. */ | |
89 | + if (cmd == ETHTOOL_GRXFH) | |
90 | + info_size = (offsetof(struct ethtool_rxnfc, data) + | |
91 | + sizeof(info.data)); | |
92 | + | |
93 | + if (copy_from_user(&info, useraddr, info_size)) | |
94 | return -EFAULT; | |
95 | ||
96 | if (info.cmd == ETHTOOL_GRXCLSRLALL) { | |
97 | @@ -257,7 +277,7 @@ static int ethtool_get_rxnfc(struct net_ | |
98 | goto err_out; | |
99 | ||
100 | ret = -EFAULT; | |
101 | - if (copy_to_user(useraddr, &info, sizeof(info))) | |
102 | + if (copy_to_user(useraddr, &info, info_size)) | |
103 | goto err_out; | |
104 | ||
105 | if (rule_buf) { | |
106 | @@ -1112,12 +1132,12 @@ int dev_ethtool(struct net *net, struct | |
107 | case ETHTOOL_GRXCLSRLCNT: | |
108 | case ETHTOOL_GRXCLSRULE: | |
109 | case ETHTOOL_GRXCLSRLALL: | |
110 | - rc = ethtool_get_rxnfc(dev, useraddr); | |
111 | + rc = ethtool_get_rxnfc(dev, ethcmd, useraddr); | |
112 | break; | |
113 | case ETHTOOL_SRXFH: | |
114 | case ETHTOOL_SRXCLSRLDEL: | |
115 | case ETHTOOL_SRXCLSRLINS: | |
116 | - rc = ethtool_set_rxnfc(dev, useraddr); | |
117 | + rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); | |
118 | break; | |
119 | case ETHTOOL_GGRO: | |
120 | rc = ethtool_get_gro(dev, useraddr); |