diff options
author | Girish Moodalbail <girish.moodalbail@oracle.com> | 2017-11-17 02:16:17 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-11-17 20:37:00 -0500 |
commit | fe18da60500b8b8aa2621fb1de7132cb1f0aebcf (patch) | |
tree | da29d087913ef4c822a8003aa0415b8bac1f5d11 /drivers/net/ipvlan/ipvlan_main.c | |
parent | cebe84c6190d741045a322f5343f717139993c08 (diff) |
ipvlan: NULL pointer dereference panic in ipvlan_port_destroy
When call to register_netdevice() (called from ipvlan_link_new()) fails,
we call ipvlan_uninit() (through ndo_uninit()) to destroy the ipvlan
port. After returning unsuccessfully from register_netdevice() we go
ahead and call ipvlan_port_destroy() again which causes NULL pointer
dereference panic. Fix the issue by making ipvlan_init() and
ipvlan_uninit() call symmetric.
The ipvlan port will now be created inside ipvlan_init() and will be
destroyed in ipvlan_uninit().
Fixes: 2ad7bf363841 (ipvlan: Initial check-in of the IPVLAN driver)
Signed-off-by: Girish Moodalbail <girish.moodalbail@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ipvlan/ipvlan_main.c')
-rw-r--r-- | drivers/net/ipvlan/ipvlan_main.c | 104 |
1 files changed, 55 insertions, 49 deletions
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index a266aa435d4d..30cb803e2fe5 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c | |||
@@ -107,16 +107,6 @@ static int ipvlan_port_create(struct net_device *dev) | |||
107 | struct ipvl_port *port; | 107 | struct ipvl_port *port; |
108 | int err, idx; | 108 | int err, idx; |
109 | 109 | ||
110 | if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK) { | ||
111 | netdev_err(dev, "Master is either lo or non-ether device\n"); | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | |||
115 | if (netdev_is_rx_handler_busy(dev)) { | ||
116 | netdev_err(dev, "Device is already in use.\n"); | ||
117 | return -EBUSY; | ||
118 | } | ||
119 | |||
120 | port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL); | 110 | port = kzalloc(sizeof(struct ipvl_port), GFP_KERNEL); |
121 | if (!port) | 111 | if (!port) |
122 | return -ENOMEM; | 112 | return -ENOMEM; |
@@ -179,8 +169,9 @@ static void ipvlan_port_destroy(struct net_device *dev) | |||
179 | static int ipvlan_init(struct net_device *dev) | 169 | static int ipvlan_init(struct net_device *dev) |
180 | { | 170 | { |
181 | struct ipvl_dev *ipvlan = netdev_priv(dev); | 171 | struct ipvl_dev *ipvlan = netdev_priv(dev); |
182 | const struct net_device *phy_dev = ipvlan->phy_dev; | 172 | struct net_device *phy_dev = ipvlan->phy_dev; |
183 | struct ipvl_port *port = ipvlan->port; | 173 | struct ipvl_port *port; |
174 | int err; | ||
184 | 175 | ||
185 | dev->state = (dev->state & ~IPVLAN_STATE_MASK) | | 176 | dev->state = (dev->state & ~IPVLAN_STATE_MASK) | |
186 | (phy_dev->state & IPVLAN_STATE_MASK); | 177 | (phy_dev->state & IPVLAN_STATE_MASK); |
@@ -196,18 +187,27 @@ static int ipvlan_init(struct net_device *dev) | |||
196 | if (!ipvlan->pcpu_stats) | 187 | if (!ipvlan->pcpu_stats) |
197 | return -ENOMEM; | 188 | return -ENOMEM; |
198 | 189 | ||
190 | if (!netif_is_ipvlan_port(phy_dev)) { | ||
191 | err = ipvlan_port_create(phy_dev); | ||
192 | if (err < 0) { | ||
193 | free_percpu(ipvlan->pcpu_stats); | ||
194 | return err; | ||
195 | } | ||
196 | } | ||
197 | port = ipvlan_port_get_rtnl(phy_dev); | ||
199 | port->count += 1; | 198 | port->count += 1; |
200 | |||
201 | return 0; | 199 | return 0; |
202 | } | 200 | } |
203 | 201 | ||
204 | static void ipvlan_uninit(struct net_device *dev) | 202 | static void ipvlan_uninit(struct net_device *dev) |
205 | { | 203 | { |
206 | struct ipvl_dev *ipvlan = netdev_priv(dev); | 204 | struct ipvl_dev *ipvlan = netdev_priv(dev); |
207 | struct ipvl_port *port = ipvlan->port; | 205 | struct net_device *phy_dev = ipvlan->phy_dev; |
206 | struct ipvl_port *port; | ||
208 | 207 | ||
209 | free_percpu(ipvlan->pcpu_stats); | 208 | free_percpu(ipvlan->pcpu_stats); |
210 | 209 | ||
210 | port = ipvlan_port_get_rtnl(phy_dev); | ||
211 | port->count -= 1; | 211 | port->count -= 1; |
212 | if (!port->count) | 212 | if (!port->count) |
213 | ipvlan_port_destroy(port->dev); | 213 | ipvlan_port_destroy(port->dev); |
@@ -554,7 +554,6 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
554 | struct net_device *phy_dev; | 554 | struct net_device *phy_dev; |
555 | int err; | 555 | int err; |
556 | u16 mode = IPVLAN_MODE_L3; | 556 | u16 mode = IPVLAN_MODE_L3; |
557 | bool create = false; | ||
558 | 557 | ||
559 | if (!tb[IFLA_LINK]) | 558 | if (!tb[IFLA_LINK]) |
560 | return -EINVAL; | 559 | return -EINVAL; |
@@ -568,28 +567,41 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
568 | 567 | ||
569 | phy_dev = tmp->phy_dev; | 568 | phy_dev = tmp->phy_dev; |
570 | } else if (!netif_is_ipvlan_port(phy_dev)) { | 569 | } else if (!netif_is_ipvlan_port(phy_dev)) { |
571 | err = ipvlan_port_create(phy_dev); | 570 | /* Exit early if the underlying link is invalid or busy */ |
572 | if (err < 0) | 571 | if (phy_dev->type != ARPHRD_ETHER || |
573 | return err; | 572 | phy_dev->flags & IFF_LOOPBACK) { |
574 | create = true; | 573 | netdev_err(phy_dev, |
575 | } | 574 | "Master is either lo or non-ether device\n"); |
575 | return -EINVAL; | ||
576 | } | ||
576 | 577 | ||
577 | if (data && data[IFLA_IPVLAN_MODE]) | 578 | if (netdev_is_rx_handler_busy(phy_dev)) { |
578 | mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); | 579 | netdev_err(phy_dev, "Device is already in use.\n"); |
580 | return -EBUSY; | ||
581 | } | ||
582 | } | ||
579 | 583 | ||
580 | port = ipvlan_port_get_rtnl(phy_dev); | ||
581 | ipvlan->phy_dev = phy_dev; | 584 | ipvlan->phy_dev = phy_dev; |
582 | ipvlan->dev = dev; | 585 | ipvlan->dev = dev; |
583 | ipvlan->port = port; | ||
584 | ipvlan->sfeatures = IPVLAN_FEATURES; | 586 | ipvlan->sfeatures = IPVLAN_FEATURES; |
585 | ipvlan_adjust_mtu(ipvlan, phy_dev); | 587 | ipvlan_adjust_mtu(ipvlan, phy_dev); |
586 | INIT_LIST_HEAD(&ipvlan->addrs); | 588 | INIT_LIST_HEAD(&ipvlan->addrs); |
587 | 589 | ||
588 | /* Flags are per port and latest update overrides. User has | 590 | /* TODO Probably put random address here to be presented to the |
589 | * to be consistent in setting it just like the mode attribute. | 591 | * world but keep using the physical-dev address for the outgoing |
592 | * packets. | ||
590 | */ | 593 | */ |
591 | if (data && data[IFLA_IPVLAN_FLAGS]) | 594 | memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); |
592 | ipvlan->port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); | 595 | |
596 | dev->priv_flags |= IFF_IPVLAN_SLAVE; | ||
597 | |||
598 | err = register_netdevice(dev); | ||
599 | if (err < 0) | ||
600 | return err; | ||
601 | |||
602 | /* ipvlan_init() would have created the port, if required */ | ||
603 | port = ipvlan_port_get_rtnl(phy_dev); | ||
604 | ipvlan->port = port; | ||
593 | 605 | ||
594 | /* If the port-id base is at the MAX value, then wrap it around and | 606 | /* If the port-id base is at the MAX value, then wrap it around and |
595 | * begin from 0x1 again. This may be due to a busy system where lots | 607 | * begin from 0x1 again. This may be due to a busy system where lots |
@@ -609,31 +621,28 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
609 | err = ida_simple_get(&port->ida, 0x1, port->dev_id_start, | 621 | err = ida_simple_get(&port->ida, 0x1, port->dev_id_start, |
610 | GFP_KERNEL); | 622 | GFP_KERNEL); |
611 | if (err < 0) | 623 | if (err < 0) |
612 | goto destroy_ipvlan_port; | 624 | goto unregister_netdev; |
613 | dev->dev_id = err; | 625 | dev->dev_id = err; |
626 | |||
614 | /* Increment id-base to the next slot for the future assignment */ | 627 | /* Increment id-base to the next slot for the future assignment */ |
615 | port->dev_id_start = err + 1; | 628 | port->dev_id_start = err + 1; |
616 | 629 | ||
617 | /* TODO Probably put random address here to be presented to the | 630 | err = netdev_upper_dev_link(phy_dev, dev, extack); |
618 | * world but keep using the physical-dev address for the outgoing | 631 | if (err) |
619 | * packets. | 632 | goto remove_ida; |
620 | */ | ||
621 | memcpy(dev->dev_addr, phy_dev->dev_addr, ETH_ALEN); | ||
622 | 633 | ||
623 | dev->priv_flags |= IFF_IPVLAN_SLAVE; | 634 | /* Flags are per port and latest update overrides. User has |
635 | * to be consistent in setting it just like the mode attribute. | ||
636 | */ | ||
637 | if (data && data[IFLA_IPVLAN_FLAGS]) | ||
638 | port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); | ||
624 | 639 | ||
625 | err = register_netdevice(dev); | 640 | if (data && data[IFLA_IPVLAN_MODE]) |
626 | if (err < 0) | 641 | mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); |
627 | goto remove_ida; | ||
628 | 642 | ||
629 | err = netdev_upper_dev_link(phy_dev, dev, extack); | ||
630 | if (err) { | ||
631 | goto unregister_netdev; | ||
632 | } | ||
633 | err = ipvlan_set_port_mode(port, mode); | 643 | err = ipvlan_set_port_mode(port, mode); |
634 | if (err) { | 644 | if (err) |
635 | goto unlink_netdev; | 645 | goto unlink_netdev; |
636 | } | ||
637 | 646 | ||
638 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); | 647 | list_add_tail_rcu(&ipvlan->pnode, &port->ipvlans); |
639 | netif_stacked_transfer_operstate(phy_dev, dev); | 648 | netif_stacked_transfer_operstate(phy_dev, dev); |
@@ -641,13 +650,10 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, | |||
641 | 650 | ||
642 | unlink_netdev: | 651 | unlink_netdev: |
643 | netdev_upper_dev_unlink(phy_dev, dev); | 652 | netdev_upper_dev_unlink(phy_dev, dev); |
644 | unregister_netdev: | ||
645 | unregister_netdevice(dev); | ||
646 | remove_ida: | 653 | remove_ida: |
647 | ida_simple_remove(&port->ida, dev->dev_id); | 654 | ida_simple_remove(&port->ida, dev->dev_id); |
648 | destroy_ipvlan_port: | 655 | unregister_netdev: |
649 | if (create) | 656 | unregister_netdevice(dev); |
650 | ipvlan_port_destroy(phy_dev); | ||
651 | return err; | 657 | return err; |
652 | } | 658 | } |
653 | EXPORT_SYMBOL_GPL(ipvlan_link_new); | 659 | EXPORT_SYMBOL_GPL(ipvlan_link_new); |