0
0
mirror of https://mirrors.bfsu.edu.cn/git/linux.git synced 2024-12-23 19:14:30 +08:00

net: phy: Check phydev->drv

There are number of function calls, originating from user-space,
typically through the Ethernet driver that can make us crash by
dereferencing phydev->drv which will be NULL once we unbind the driver
from the PHY.

There are still functional issues that prevent an unbind then rebind to
work, but these will be addressed separately.

Suggested-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Florian Fainelli 2017-02-17 16:07:34 -08:00 committed by David S. Miller
parent 7b9a88a390
commit 25149ef9d2
3 changed files with 28 additions and 7 deletions
drivers/net/phy
include/linux

View File

@ -580,7 +580,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
return 0; return 0;
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
if (phydev->drv->hwtstamp) if (phydev->drv && phydev->drv->hwtstamp)
return phydev->drv->hwtstamp(phydev, ifr); return phydev->drv->hwtstamp(phydev, ifr);
/* fall through */ /* fall through */
@ -603,6 +603,9 @@ int phy_start_aneg(struct phy_device *phydev)
{ {
int err; int err;
if (!phydev->drv)
return -EIO;
mutex_lock(&phydev->lock); mutex_lock(&phydev->lock);
if (AUTONEG_DISABLE == phydev->autoneg) if (AUTONEG_DISABLE == phydev->autoneg)
@ -975,7 +978,7 @@ void phy_state_machine(struct work_struct *work)
old_state = phydev->state; old_state = phydev->state;
if (phydev->drv->link_change_notify) if (phydev->drv && phydev->drv->link_change_notify)
phydev->drv->link_change_notify(phydev); phydev->drv->link_change_notify(phydev);
switch (phydev->state) { switch (phydev->state) {
@ -1286,6 +1289,9 @@ EXPORT_SYMBOL(phy_write_mmd_indirect);
*/ */
int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
{ {
if (!phydev->drv)
return -EIO;
/* According to 802.3az,the EEE is supported only in full duplex-mode. /* According to 802.3az,the EEE is supported only in full duplex-mode.
* Also EEE feature is active when core is operating with MII, GMII * Also EEE feature is active when core is operating with MII, GMII
* or RGMII (all kinds). Internal PHYs are also allowed to proceed and * or RGMII (all kinds). Internal PHYs are also allowed to proceed and
@ -1363,6 +1369,9 @@ EXPORT_SYMBOL(phy_init_eee);
*/ */
int phy_get_eee_err(struct phy_device *phydev) int phy_get_eee_err(struct phy_device *phydev)
{ {
if (!phydev->drv)
return -EIO;
return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS); return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS);
} }
EXPORT_SYMBOL(phy_get_eee_err); EXPORT_SYMBOL(phy_get_eee_err);
@ -1379,6 +1388,9 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
{ {
int val; int val;
if (!phydev->drv)
return -EIO;
/* Get Supported EEE */ /* Get Supported EEE */
val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS); val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS);
if (val < 0) if (val < 0)
@ -1412,6 +1424,9 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
{ {
int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
if (!phydev->drv)
return -EIO;
/* Mask prohibited EEE modes */ /* Mask prohibited EEE modes */
val &= ~phydev->eee_broken_modes; val &= ~phydev->eee_broken_modes;
@ -1423,7 +1438,7 @@ EXPORT_SYMBOL(phy_ethtool_set_eee);
int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{ {
if (phydev->drv->set_wol) if (phydev->drv && phydev->drv->set_wol)
return phydev->drv->set_wol(phydev, wol); return phydev->drv->set_wol(phydev, wol);
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -1432,7 +1447,7 @@ EXPORT_SYMBOL(phy_ethtool_set_wol);
void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{ {
if (phydev->drv->get_wol) if (phydev->drv && phydev->drv->get_wol)
phydev->drv->get_wol(phydev, wol); phydev->drv->get_wol(phydev, wol);
} }
EXPORT_SYMBOL(phy_ethtool_get_wol); EXPORT_SYMBOL(phy_ethtool_get_wol);
@ -1468,6 +1483,9 @@ int phy_ethtool_nway_reset(struct net_device *ndev)
if (!phydev) if (!phydev)
return -ENODEV; return -ENODEV;
if (!phydev->drv)
return -EIO;
return genphy_restart_aneg(phydev); return genphy_restart_aneg(phydev);
} }
EXPORT_SYMBOL(phy_ethtool_nway_reset); EXPORT_SYMBOL(phy_ethtool_nway_reset);

View File

@ -1094,7 +1094,7 @@ int phy_suspend(struct phy_device *phydev)
if (wol.wolopts) if (wol.wolopts)
return -EBUSY; return -EBUSY;
if (phydrv->suspend) if (phydev->drv && phydrv->suspend)
ret = phydrv->suspend(phydev); ret = phydrv->suspend(phydev);
if (ret) if (ret)
@ -1111,7 +1111,7 @@ int phy_resume(struct phy_device *phydev)
struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
int ret = 0; int ret = 0;
if (phydrv->resume) if (phydev->drv && phydrv->resume)
ret = phydrv->resume(phydev); ret = phydrv->resume(phydev);
if (ret) if (ret)
@ -1790,7 +1790,7 @@ static int phy_remove(struct device *dev)
phydev->state = PHY_DOWN; phydev->state = PHY_DOWN;
mutex_unlock(&phydev->lock); mutex_unlock(&phydev->lock);
if (phydev->drv->remove) if (phydev->drv && phydev->drv->remove)
phydev->drv->remove(phydev); phydev->drv->remove(phydev);
phydev->drv = NULL; phydev->drv = NULL;

View File

@ -807,6 +807,9 @@ int phy_stop_interrupts(struct phy_device *phydev);
static inline int phy_read_status(struct phy_device *phydev) static inline int phy_read_status(struct phy_device *phydev)
{ {
if (!phydev->drv)
return -EIO;
return phydev->drv->read_status(phydev); return phydev->drv->read_status(phydev);
} }