aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/ethtool.c
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2011-12-15 08:55:01 -0500
committerDavid S. Miller <davem@davemloft.net>2011-12-16 13:52:47 -0500
commit7850f63f1620512631445b901ae11cd149e7375c (patch)
treed26a5f049dcf3634c4bf9e1b86915d201fab3836 /net/core/ethtool.c
parent14596f7006297b67516e2b6a2b26bcb11fe08fb3 (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.c81
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:
515static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, 515static 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
548out: 558out:
@@ -553,32 +563,51 @@ out:
553static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, 563static 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
584out: 613out: