Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: fix phylink_start()/phylink_stop() calls

Place phylink_start()/phylink_stop() inside dsa_port_enable() and
dsa_port_disable(), which ensures that we call phylink_stop() before
tearing down phylink - which is a documented requirement. Failure
to do so can cause use-after-free bugs.

Fixes: 0e27921816ad ("net: dsa: Use PHYLINK for the CPU/DSA ports")
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Russell King and committed by
David S. Miller
8640f8dc f650bcd4

+30 -12
+2
net/dsa/dsa_priv.h
··· 117 117 /* port.c */ 118 118 int dsa_port_set_state(struct dsa_port *dp, u8 state, 119 119 struct switchdev_trans *trans); 120 + int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy); 120 121 int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy); 122 + void dsa_port_disable_rt(struct dsa_port *dp); 121 123 void dsa_port_disable(struct dsa_port *dp); 122 124 int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br); 123 125 void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br);
+26 -6
net/dsa/port.c
··· 63 63 pr_err("DSA: failed to set STP state %u (%d)\n", state, err); 64 64 } 65 65 66 - int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 66 + int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy) 67 67 { 68 68 struct dsa_switch *ds = dp->ds; 69 69 int port = dp->index; ··· 78 78 if (!dp->bridge_dev) 79 79 dsa_port_set_state_now(dp, BR_STATE_FORWARDING); 80 80 81 + if (dp->pl) 82 + phylink_start(dp->pl); 83 + 81 84 return 0; 82 85 } 83 86 84 - void dsa_port_disable(struct dsa_port *dp) 87 + int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) 88 + { 89 + int err; 90 + 91 + rtnl_lock(); 92 + err = dsa_port_enable_rt(dp, phy); 93 + rtnl_unlock(); 94 + 95 + return err; 96 + } 97 + 98 + void dsa_port_disable_rt(struct dsa_port *dp) 85 99 { 86 100 struct dsa_switch *ds = dp->ds; 87 101 int port = dp->index; 102 + 103 + if (dp->pl) 104 + phylink_stop(dp->pl); 88 105 89 106 if (!dp->bridge_dev) 90 107 dsa_port_set_state_now(dp, BR_STATE_DISABLED); 91 108 92 109 if (ds->ops->port_disable) 93 110 ds->ops->port_disable(ds, port); 111 + } 112 + 113 + void dsa_port_disable(struct dsa_port *dp) 114 + { 115 + rtnl_lock(); 116 + dsa_port_disable_rt(dp); 117 + rtnl_unlock(); 94 118 } 95 119 96 120 int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) ··· 637 613 pr_err("could not attach to PHY: %d\n", err); 638 614 goto err_phy_connect; 639 615 } 640 - 641 - rtnl_lock(); 642 - phylink_start(dp->pl); 643 - rtnl_unlock(); 644 616 645 617 return 0; 646 618
+2 -6
net/dsa/slave.c
··· 88 88 goto clear_allmulti; 89 89 } 90 90 91 - err = dsa_port_enable(dp, dev->phydev); 91 + err = dsa_port_enable_rt(dp, dev->phydev); 92 92 if (err) 93 93 goto clear_promisc; 94 - 95 - phylink_start(dp->pl); 96 94 97 95 return 0; 98 96 ··· 112 114 struct net_device *master = dsa_slave_to_master(dev); 113 115 struct dsa_port *dp = dsa_slave_to_port(dev); 114 116 115 - phylink_stop(dp->pl); 116 - 117 - dsa_port_disable(dp); 117 + dsa_port_disable_rt(dp); 118 118 119 119 dev_mc_unsync(master, dev); 120 120 dev_uc_unsync(master, dev);