Commit d5cd92448fded12c91f7574e49747c5f7d975a8d
Committed by
David S. Miller
1 parent
ac0a121d79
Exists in
master
and in
39 other branches
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>
Showing 1 changed file with 12 additions and 6 deletions Side-by-side Diff
drivers/net/macvlan.c
... | ... | @@ -39,8 +39,11 @@ |
39 | 39 | struct list_head vlans; |
40 | 40 | struct rcu_head rcu; |
41 | 41 | bool passthru; |
42 | + int count; | |
42 | 43 | }; |
43 | 44 | |
45 | +static void macvlan_port_destroy(struct net_device *dev); | |
46 | + | |
44 | 47 | #define macvlan_port_get_rcu(dev) \ |
45 | 48 | ((struct macvlan_port *) rcu_dereference(dev->rx_handler_data)) |
46 | 49 | #define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data) |
47 | 50 | |
... | ... | @@ -457,8 +460,13 @@ |
457 | 460 | static void macvlan_uninit(struct net_device *dev) |
458 | 461 | { |
459 | 462 | struct macvlan_dev *vlan = netdev_priv(dev); |
463 | + struct macvlan_port *port = vlan->port; | |
460 | 464 | |
461 | 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 | 472 | static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, |
465 | 473 | |
... | ... | @@ -691,12 +699,13 @@ |
691 | 699 | vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]); |
692 | 700 | |
693 | 701 | if (vlan->mode == MACVLAN_MODE_PASSTHRU) { |
694 | - if (!list_empty(&port->vlans)) | |
702 | + if (port->count) | |
695 | 703 | return -EINVAL; |
696 | 704 | port->passthru = true; |
697 | 705 | memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN); |
698 | 706 | } |
699 | 707 | |
708 | + port->count += 1; | |
700 | 709 | err = register_netdevice(dev); |
701 | 710 | if (err < 0) |
702 | 711 | goto destroy_port; |
... | ... | @@ -707,7 +716,8 @@ |
707 | 716 | return 0; |
708 | 717 | |
709 | 718 | destroy_port: |
710 | - if (list_empty(&port->vlans)) | |
719 | + port->count -= 1; | |
720 | + if (!port->count) | |
711 | 721 | macvlan_port_destroy(lowerdev); |
712 | 722 | |
713 | 723 | return err; |
714 | 724 | |
... | ... | @@ -725,13 +735,9 @@ |
725 | 735 | void macvlan_dellink(struct net_device *dev, struct list_head *head) |
726 | 736 | { |
727 | 737 | struct macvlan_dev *vlan = netdev_priv(dev); |
728 | - struct macvlan_port *port = vlan->port; | |
729 | 738 | |
730 | 739 | list_del(&vlan->list); |
731 | 740 | unregister_netdevice_queue(dev, head); |
732 | - | |
733 | - if (list_empty(&port->vlans)) | |
734 | - macvlan_port_destroy(port->dev); | |
735 | 741 | } |
736 | 742 | EXPORT_SYMBOL_GPL(macvlan_dellink); |
737 | 743 |