diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2011-12-15 08:55:01 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-12-16 13:52:47 -0500 |
commit | 7850f63f1620512631445b901ae11cd149e7375c (patch) | |
tree | d26a5f049dcf3634c4bf9e1b86915d201fab3836 /net/core/ethtool.c | |
parent | 14596f7006297b67516e2b6a2b26bcb11fe08fb3 (diff) |
ethtool: Centralise validation of ETHTOOL_{G, S}RXFHINDIR parameters
Add a new ethtool operation (get_rxfh_indir_size) to get the
indirectional table size. Use this to validate the user buffer size
before calling get_rxfh_indir or set_rxfh_indir. Use get_rxnfc to get
the number of RX rings, and validate the contents of the new
indirection table before calling set_rxfh_indir. Remove this
validation from drivers.
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Acked-by: Dimitris Michailidis <dm@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/ethtool.c')
-rw-r--r-- | net/core/ethtool.c | 81 |
1 files changed, 55 insertions, 26 deletions
diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 31b0b7f5383e..69f71b86b035 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c | |||
@@ -515,34 +515,44 @@ err_out: | |||
515 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, | 515 | static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, |
516 | void __user *useraddr) | 516 | void __user *useraddr) |
517 | { | 517 | { |
518 | struct ethtool_rxfh_indir *indir; | 518 | u32 user_size, dev_size; |
519 | u32 table_size; | 519 | u32 *indir; |
520 | size_t full_size; | ||
521 | int ret; | 520 | int ret; |
522 | 521 | ||
523 | if (!dev->ethtool_ops->get_rxfh_indir) | 522 | if (!dev->ethtool_ops->get_rxfh_indir_size || |
523 | !dev->ethtool_ops->get_rxfh_indir) | ||
524 | return -EOPNOTSUPP; | ||
525 | dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); | ||
526 | if (dev_size == 0) | ||
524 | return -EOPNOTSUPP; | 527 | return -EOPNOTSUPP; |
525 | 528 | ||
526 | if (copy_from_user(&table_size, | 529 | if (copy_from_user(&user_size, |
527 | useraddr + offsetof(struct ethtool_rxfh_indir, size), | 530 | useraddr + offsetof(struct ethtool_rxfh_indir, size), |
528 | sizeof(table_size))) | 531 | sizeof(user_size))) |
529 | return -EFAULT; | 532 | return -EFAULT; |
530 | 533 | ||
531 | if (table_size > | 534 | if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh_indir, size), |
532 | (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index)) | 535 | &dev_size, sizeof(dev_size))) |
533 | return -ENOMEM; | 536 | return -EFAULT; |
534 | full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size; | 537 | |
535 | indir = kzalloc(full_size, GFP_USER); | 538 | /* If the user buffer size is 0, this is just a query for the |
539 | * device table size. Otherwise, if it's smaller than the | ||
540 | * device table size it's an error. | ||
541 | */ | ||
542 | if (user_size < dev_size) | ||
543 | return user_size == 0 ? 0 : -EINVAL; | ||
544 | |||
545 | indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); | ||
536 | if (!indir) | 546 | if (!indir) |
537 | return -ENOMEM; | 547 | return -ENOMEM; |
538 | 548 | ||
539 | indir->cmd = ETHTOOL_GRXFHINDIR; | ||
540 | indir->size = table_size; | ||
541 | ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); | 549 | ret = dev->ethtool_ops->get_rxfh_indir(dev, indir); |
542 | if (ret) | 550 | if (ret) |
543 | goto out; | 551 | goto out; |
544 | 552 | ||
545 | if (copy_to_user(useraddr, indir, full_size)) | 553 | if (copy_to_user(useraddr + |
554 | offsetof(struct ethtool_rxfh_indir, ring_index[0]), | ||
555 | indir, dev_size * sizeof(indir[0]))) | ||
546 | ret = -EFAULT; | 556 | ret = -EFAULT; |
547 | 557 | ||
548 | out: | 558 | out: |
@@ -553,32 +563,51 @@ out: | |||
553 | static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, | 563 | static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, |
554 | void __user *useraddr) | 564 | void __user *useraddr) |
555 | { | 565 | { |
556 | struct ethtool_rxfh_indir *indir; | 566 | struct ethtool_rxnfc rx_rings; |
557 | u32 table_size; | 567 | u32 user_size, dev_size, i; |
558 | size_t full_size; | 568 | u32 *indir; |
559 | int ret; | 569 | int ret; |
560 | 570 | ||
561 | if (!dev->ethtool_ops->set_rxfh_indir) | 571 | if (!dev->ethtool_ops->get_rxfh_indir_size || |
572 | !dev->ethtool_ops->set_rxfh_indir || | ||
573 | !dev->ethtool_ops->get_rxnfc) | ||
574 | return -EOPNOTSUPP; | ||
575 | dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); | ||
576 | if (dev_size == 0) | ||
562 | return -EOPNOTSUPP; | 577 | return -EOPNOTSUPP; |
563 | 578 | ||
564 | if (copy_from_user(&table_size, | 579 | if (copy_from_user(&user_size, |
565 | useraddr + offsetof(struct ethtool_rxfh_indir, size), | 580 | useraddr + offsetof(struct ethtool_rxfh_indir, size), |
566 | sizeof(table_size))) | 581 | sizeof(user_size))) |
567 | return -EFAULT; | 582 | return -EFAULT; |
568 | 583 | ||
569 | if (table_size > | 584 | if (user_size != dev_size) |
570 | (KMALLOC_MAX_SIZE - sizeof(*indir)) / sizeof(*indir->ring_index)) | 585 | return -EINVAL; |
571 | return -ENOMEM; | 586 | |
572 | full_size = sizeof(*indir) + sizeof(*indir->ring_index) * table_size; | 587 | indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); |
573 | indir = kmalloc(full_size, GFP_USER); | ||
574 | if (!indir) | 588 | if (!indir) |
575 | return -ENOMEM; | 589 | return -ENOMEM; |
576 | 590 | ||
577 | if (copy_from_user(indir, useraddr, full_size)) { | 591 | if (copy_from_user(indir, |
592 | useraddr + | ||
593 | offsetof(struct ethtool_rxfh_indir, ring_index[0]), | ||
594 | dev_size * sizeof(indir[0]))) { | ||
578 | ret = -EFAULT; | 595 | ret = -EFAULT; |
579 | goto out; | 596 | goto out; |
580 | } | 597 | } |
581 | 598 | ||
599 | /* Validate ring indices */ | ||
600 | rx_rings.cmd = ETHTOOL_GRXRINGS; | ||
601 | ret = dev->ethtool_ops->get_rxnfc(dev, &rx_rings, NULL); | ||
602 | if (ret) | ||
603 | goto out; | ||
604 | for (i = 0; i < dev_size; i++) { | ||
605 | if (indir[i] >= rx_rings.data) { | ||
606 | ret = -EINVAL; | ||
607 | goto out; | ||
608 | } | ||
609 | } | ||
610 | |||
582 | ret = dev->ethtool_ops->set_rxfh_indir(dev, indir); | 611 | ret = dev->ethtool_ops->set_rxfh_indir(dev, indir); |
583 | 612 | ||
584 | out: | 613 | out: |