diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2010-06-28 04:45:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-29 04:00:29 -0400 |
commit | bf988435bd5b53529f4408a8efb1f433f6ddfda9 (patch) | |
tree | 7805f577d019f587146b4fd1cf642faacfec2f14 | |
parent | db048b69037e7fa6a7d9e95a1271a50dc08ae233 (diff) |
ethtool: Fix potential user buffer overflow for ETHTOOL_{G, S}RXFH
struct ethtool_rxnfc was originally defined in 2.6.27 for the
ETHTOOL_{G,S}RXFH command with only the cmd, flow_type and data
fields. It was then extended in 2.6.30 to support various additional
commands. These commands should have been defined to use a new
structure, but it is too late to change that now.
Since user-space may still be using the old structure definition
for the ETHTOOL_{G,S}RXFH commands, and since they do not need the
additional fields, only copy the originally defined fields to and
from user-space.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Cc: stable@kernel.org
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/ethtool.h | 2 | ||||
-rw-r--r-- | net/core/ethtool.c | 36 |
2 files changed, 29 insertions, 9 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 276b40a16835..b4207ca3ad52 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h | |||
@@ -379,6 +379,8 @@ struct ethtool_rxnfc { | |||
379 | __u32 flow_type; | 379 | __u32 flow_type; |
380 | /* The rx flow hash value or the rule DB size */ | 380 | /* The rx flow hash value or the rule DB size */ |
381 | __u64 data; | 381 | __u64 data; |
382 | /* The following fields are not valid and must not be used for | ||
383 | * the ETHTOOL_{G,X}RXFH commands. */ | ||
382 | struct ethtool_rx_flow_spec fs; | 384 | struct ethtool_rx_flow_spec fs; |
383 | __u32 rule_cnt; | 385 | __u32 rule_cnt; |
384 | __u32 rule_locs[0]; | 386 | __u32 rule_locs[0]; |
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index a3a7e9a48dff..75e4ffeb8cc9 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -318,23 +318,33 @@ out: | |||
318 | } | 318 | } |
319 | 319 | ||
320 | static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, | 320 | static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, |
321 | void __user *useraddr) | 321 | u32 cmd, void __user *useraddr) |
322 | { | 322 | { |
323 | struct ethtool_rxnfc cmd; | 323 | struct ethtool_rxnfc info; |
324 | size_t info_size = sizeof(info); | ||
324 | 325 | ||
325 | if (!dev->ethtool_ops->set_rxnfc) | 326 | if (!dev->ethtool_ops->set_rxnfc) |
326 | return -EOPNOTSUPP; | 327 | return -EOPNOTSUPP; |
327 | 328 | ||
328 | if (copy_from_user(&cmd, useraddr, sizeof(cmd))) | 329 | /* struct ethtool_rxnfc was originally defined for |
330 | * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data | ||
331 | * members. User-space might still be using that | ||
332 | * definition. */ | ||
333 | if (cmd == ETHTOOL_SRXFH) | ||
334 | info_size = (offsetof(struct ethtool_rxnfc, data) + | ||
335 | sizeof(info.data)); | ||
336 | |||
337 | if (copy_from_user(&info, useraddr, info_size)) | ||
329 | return -EFAULT; | 338 | return -EFAULT; |
330 | 339 | ||
331 | return dev->ethtool_ops->set_rxnfc(dev, &cmd); | 340 | return dev->ethtool_ops->set_rxnfc(dev, &info); |
332 | } | 341 | } |
333 | 342 | ||
334 | static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, | 343 | static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, |
335 | void __user *useraddr) | 344 | u32 cmd, void __user *useraddr) |
336 | { | 345 | { |
337 | struct ethtool_rxnfc info; | 346 | struct ethtool_rxnfc info; |
347 | size_t info_size = sizeof(info); | ||
338 | const struct ethtool_ops *ops = dev->ethtool_ops; | 348 | const struct ethtool_ops *ops = dev->ethtool_ops; |
339 | int ret; | 349 | int ret; |
340 | void *rule_buf = NULL; | 350 | void *rule_buf = NULL; |
@@ -342,7 +352,15 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, | |||
342 | if (!ops->get_rxnfc) | 352 | if (!ops->get_rxnfc) |
343 | return -EOPNOTSUPP; | 353 | return -EOPNOTSUPP; |
344 | 354 | ||
345 | if (copy_from_user(&info, useraddr, sizeof(info))) | 355 | /* struct ethtool_rxnfc was originally defined for |
356 | * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data | ||
357 | * members. User-space might still be using that | ||
358 | * definition. */ | ||
359 | if (cmd == ETHTOOL_GRXFH) | ||
360 | info_size = (offsetof(struct ethtool_rxnfc, data) + | ||
361 | sizeof(info.data)); | ||
362 | |||
363 | if (copy_from_user(&info, useraddr, info_size)) | ||
346 | return -EFAULT; | 364 | return -EFAULT; |
347 | 365 | ||
348 | if (info.cmd == ETHTOOL_GRXCLSRLALL) { | 366 | if (info.cmd == ETHTOOL_GRXCLSRLALL) { |
@@ -360,7 +378,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, | |||
360 | goto err_out; | 378 | goto err_out; |
361 | 379 | ||
362 | ret = -EFAULT; | 380 | ret = -EFAULT; |
363 | if (copy_to_user(useraddr, &info, sizeof(info))) | 381 | if (copy_to_user(useraddr, &info, info_size)) |
364 | goto err_out; | 382 | goto err_out; |
365 | 383 | ||
366 | if (rule_buf) { | 384 | if (rule_buf) { |
@@ -1517,12 +1535,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) | |||
1517 | case ETHTOOL_GRXCLSRLCNT: | 1535 | case ETHTOOL_GRXCLSRLCNT: |
1518 | case ETHTOOL_GRXCLSRULE: | 1536 | case ETHTOOL_GRXCLSRULE: |
1519 | case ETHTOOL_GRXCLSRLALL: | 1537 | case ETHTOOL_GRXCLSRLALL: |
1520 | rc = ethtool_get_rxnfc(dev, useraddr); | 1538 | rc = ethtool_get_rxnfc(dev, ethcmd, useraddr); |
1521 | break; | 1539 | break; |
1522 | case ETHTOOL_SRXFH: | 1540 | case ETHTOOL_SRXFH: |
1523 | case ETHTOOL_SRXCLSRLDEL: | 1541 | case ETHTOOL_SRXCLSRLDEL: |
1524 | case ETHTOOL_SRXCLSRLINS: | 1542 | case ETHTOOL_SRXCLSRLINS: |
1525 | rc = ethtool_set_rxnfc(dev, useraddr); | 1543 | rc = ethtool_set_rxnfc(dev, ethcmd, useraddr); |
1526 | break; | 1544 | break; |
1527 | case ETHTOOL_GGRO: | 1545 | case ETHTOOL_GGRO: |
1528 | rc = ethtool_get_gro(dev, useraddr); | 1546 | rc = ethtool_get_gro(dev, useraddr); |