diff options
author | Ben Hutchings <ben.hutchings@codethink.co.uk> | 2015-01-16 12:51:25 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-01-19 15:37:40 -0500 |
commit | 4f9dce230b32eec45cec8c28cae61efdfa2f7d57 (patch) | |
tree | 685c3ae92315b1627d99a9b72e6906031f3baba8 | |
parent | b37feed7c2803cce71a746623594f19bbb5a21aa (diff) |
sh_eth: Fix ethtool operation crash when net device is down
The driver connects and disconnects the PHY device whenever the
net device is brought up and down. The ethtool get_settings,
set_settings and nway_reset operations will dereference a null
or dangling pointer if called while it is down.
I think it would be preferable to keep the PHY connected, but there
may be good reasons not to.
As an immediate fix for this bug:
- Set the phydev pointer to NULL after disconnecting the PHY
- Change those three operations to return -ENODEV while the PHY is
not connected
Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/renesas/sh_eth.c | 10 |
1 files changed, 10 insertions, 0 deletions
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 01dfae4fece0..6576243222af 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c | |||
@@ -1827,6 +1827,9 @@ static int sh_eth_get_settings(struct net_device *ndev, | |||
1827 | unsigned long flags; | 1827 | unsigned long flags; |
1828 | int ret; | 1828 | int ret; |
1829 | 1829 | ||
1830 | if (!mdp->phydev) | ||
1831 | return -ENODEV; | ||
1832 | |||
1830 | spin_lock_irqsave(&mdp->lock, flags); | 1833 | spin_lock_irqsave(&mdp->lock, flags); |
1831 | ret = phy_ethtool_gset(mdp->phydev, ecmd); | 1834 | ret = phy_ethtool_gset(mdp->phydev, ecmd); |
1832 | spin_unlock_irqrestore(&mdp->lock, flags); | 1835 | spin_unlock_irqrestore(&mdp->lock, flags); |
@@ -1841,6 +1844,9 @@ static int sh_eth_set_settings(struct net_device *ndev, | |||
1841 | unsigned long flags; | 1844 | unsigned long flags; |
1842 | int ret; | 1845 | int ret; |
1843 | 1846 | ||
1847 | if (!mdp->phydev) | ||
1848 | return -ENODEV; | ||
1849 | |||
1844 | spin_lock_irqsave(&mdp->lock, flags); | 1850 | spin_lock_irqsave(&mdp->lock, flags); |
1845 | 1851 | ||
1846 | /* disable tx and rx */ | 1852 | /* disable tx and rx */ |
@@ -1875,6 +1881,9 @@ static int sh_eth_nway_reset(struct net_device *ndev) | |||
1875 | unsigned long flags; | 1881 | unsigned long flags; |
1876 | int ret; | 1882 | int ret; |
1877 | 1883 | ||
1884 | if (!mdp->phydev) | ||
1885 | return -ENODEV; | ||
1886 | |||
1878 | spin_lock_irqsave(&mdp->lock, flags); | 1887 | spin_lock_irqsave(&mdp->lock, flags); |
1879 | ret = phy_start_aneg(mdp->phydev); | 1888 | ret = phy_start_aneg(mdp->phydev); |
1880 | spin_unlock_irqrestore(&mdp->lock, flags); | 1889 | spin_unlock_irqrestore(&mdp->lock, flags); |
@@ -2184,6 +2193,7 @@ static int sh_eth_close(struct net_device *ndev) | |||
2184 | if (mdp->phydev) { | 2193 | if (mdp->phydev) { |
2185 | phy_stop(mdp->phydev); | 2194 | phy_stop(mdp->phydev); |
2186 | phy_disconnect(mdp->phydev); | 2195 | phy_disconnect(mdp->phydev); |
2196 | mdp->phydev = NULL; | ||
2187 | } | 2197 | } |
2188 | 2198 | ||
2189 | free_irq(ndev->irq, ndev); | 2199 | free_irq(ndev->irq, ndev); |