diff options
author | Eric W. Biederman <ebiederm@aristanetworks.com> | 2011-03-21 21:22:22 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-21 21:22:22 -0400 |
commit | d5cd92448fded12c91f7574e49747c5f7d975a8d (patch) | |
tree | c65c8f66d7818b4250d56ef489cb2b3125276403 /drivers/net/macvlan.c | |
parent | ac0a121d7906b049dfee3649f886c969fbb3c1b7 (diff) |
macvlan: Fix use after free of struct macvlan_port.
When the macvlan driver was extended to call unregisgter_netdevice_queue
in 23289a37e2b127dfc4de1313fba15bb4c9f0cd5b, a use after free of struct
macvlan_port was introduced. The code in dellink relied on unregister_netdevice
actually unregistering the net device so it would be safe to free macvlan_port.
Since unregister_netdevice_queue can just queue up the unregister instead of
performing the unregiser immediately we free the macvlan_port too soon and
then the code in macvlan_stop removes the macaddress for the set of macaddress
to listen for and uses memory that has already been freed.
To fix this add a reference count to track when it is safe to free the macvlan_port
and move the call of macvlan_port_destroy into macvlan_uninit which is guaranteed
to be called after the final macvlan_port_close.
Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/macvlan.c')
-rw-r--r-- | drivers/net/macvlan.c | 18 |
1 files changed, 12 insertions, 6 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 5b37d3c191e4..78e34e9e4f00 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c | |||
@@ -39,8 +39,11 @@ struct macvlan_port { | |||
39 | struct list_head vlans; | 39 | struct list_head vlans; |
40 | struct rcu_head rcu; | 40 | struct rcu_head rcu; |
41 | bool passthru; | 41 | bool passthru; |
42 | int count; | ||
42 | }; | 43 | }; |
43 | 44 | ||
45 | static void macvlan_port_destroy(struct net_device *dev); | ||
46 | |||
44 | #define macvlan_port_get_rcu(dev) \ | 47 | #define macvlan_port_get_rcu(dev) \ |
45 | ((struct macvlan_port *) rcu_dereference(dev->rx_handler_data)) | 48 | ((struct macvlan_port *) rcu_dereference(dev->rx_handler_data)) |
46 | #define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data) | 49 | #define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data) |
@@ -457,8 +460,13 @@ static int macvlan_init(struct net_device *dev) | |||
457 | static void macvlan_uninit(struct net_device *dev) | 460 | static void macvlan_uninit(struct net_device *dev) |
458 | { | 461 | { |
459 | struct macvlan_dev *vlan = netdev_priv(dev); | 462 | struct macvlan_dev *vlan = netdev_priv(dev); |
463 | struct macvlan_port *port = vlan->port; | ||
460 | 464 | ||
461 | free_percpu(vlan->pcpu_stats); | 465 | free_percpu(vlan->pcpu_stats); |
466 | |||
467 | port->count -= 1; | ||
468 | if (!port->count) | ||
469 | macvlan_port_destroy(port->dev); | ||
462 | } | 470 | } |
463 | 471 | ||
464 | static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, | 472 | static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, |
@@ -691,12 +699,13 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, | |||
691 | vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); | 699 | vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); |
692 | 700 | ||
693 | if (vlan->mode == MACVLAN_MODE_PASSTHRU) { | 701 | if (vlan->mode == MACVLAN_MODE_PASSTHRU) { |
694 | if (!list_empty(&port->vlans)) | 702 | if (port->count) |
695 | return -EINVAL; | 703 | return -EINVAL; |
696 | port->passthru = true; | 704 | port->passthru = true; |
697 | memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN); | 705 | memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN); |
698 | } | 706 | } |
699 | 707 | ||
708 | port->count += 1; | ||
700 | err = register_netdevice(dev); | 709 | err = register_netdevice(dev); |
701 | if (err < 0) | 710 | if (err < 0) |
702 | goto destroy_port; | 711 | goto destroy_port; |
@@ -707,7 +716,8 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, | |||
707 | return 0; | 716 | return 0; |
708 | 717 | ||
709 | destroy_port: | 718 | destroy_port: |
710 | if (list_empty(&port->vlans)) | 719 | port->count -= 1; |
720 | if (!port->count) | ||
711 | macvlan_port_destroy(lowerdev); | 721 | macvlan_port_destroy(lowerdev); |
712 | 722 | ||
713 | return err; | 723 | return err; |
@@ -725,13 +735,9 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev, | |||
725 | void macvlan_dellink(struct net_device *dev, struct list_head *head) | 735 | void macvlan_dellink(struct net_device *dev, struct list_head *head) |
726 | { | 736 | { |
727 | struct macvlan_dev *vlan = netdev_priv(dev); | 737 | struct macvlan_dev *vlan = netdev_priv(dev); |
728 | struct macvlan_port *port = vlan->port; | ||
729 | 738 | ||
730 | list_del(&vlan->list); | 739 | list_del(&vlan->list); |
731 | unregister_netdevice_queue(dev, head); | 740 | unregister_netdevice_queue(dev, head); |
732 | |||
733 | if (list_empty(&port->vlans)) | ||
734 | macvlan_port_destroy(port->dev); | ||
735 | } | 741 | } |
736 | EXPORT_SYMBOL_GPL(macvlan_dellink); | 742 | EXPORT_SYMBOL_GPL(macvlan_dellink); |
737 | 743 | ||