mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] net: phy: reject attach while the PHY driver is in transition
@ 2026-09-14 20:42 Aleksei Sviridkin
  2026-09-17 11:43 ` netdev-bot+sashiko
  2026-09-17 12:00 ` Maxime Chevallier
  0 siblings, 2 replies; 6+ messages in thread
From: Aleksei Sviridkin @ 2026-09-14 20:42 UTC (permalink / raw)
  To: netdev
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	linux-kernel, Aleksei Sviridkin

phy_remove() clears phydev->drv as its last act; the driver core
clears d->driver only afterwards, in device_unbind_cleanup(). In that
window phy_attach_direct() skips the genphy substitution, because
d->driver is still set, and then dereferences the NULL phydev->drv in
phy_drv_supports_irq().

Refuse the attach there, before any reference on the driver is taken.
The function holds no lock over phydev->drv, and it cannot hold
device_lock across the attach: for a genphy-substituted PHY its error
path reaches device_release_driver() on the same device, which takes
that lock again. So this closes the case where the unbind is already
in flight; an unbind starting mid-attach still races.

Failing beats falling back to polling: phylink_bringup_phy()
dereferences phy->drv right after a successful attach, and a continued
attach would already hold the driver module reference that
phy_detach() drops only while d->driver is set, leaking it once the
unbind completes. -ENODEV is wrong: DSA takes it as permission to
look for the PHY on the switch's internal MDIO bus.

Fixes: 61c81872815f ("net: phy: phy_device: Prevent nullptr exceptions on ISR")
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
---

Notes:
    Found by reading the unbind path, not from a crash report: phy_remove()
    clears phydev->drv before the driver core clears d->driver, while
    phy_attach_direct() keys its genphy substitution off d->driver.
    
    Verified on an MT7981 board (mtk_eth_soc GMAC, "MediaTek MT7981 PHY" at
    mdio-bus:00), 6.18.44, with a 200 ms msleep() added at the end of
    phy_remove() to hold the window open. Two images, identical except for
    this patch.
    
    Without the patch, backgrounding
    
      echo mdio-bus:00 > "/sys/bus/mdio_bus/drivers/MediaTek MT7981 PHY/unbind"
    
    and immediately running "ip link set wan up" oopses on the first
    attempt:
    
      Unable to handle kernel access to user memory outside uaccess
      routines at virtual address 0000000000000128
      pc : phy_attach_direct+0x150/0x380
      Call trace:
       phy_attach_direct+0x150/0x380 (P)
       mtk_open+0x38/0xb70
    
    x0 is 0 and 0x128 is the offset of config_intr in struct phy_driver.
    
    With the patch the same sequence fails the attach on the first attempt
    instead, "wan: mtk_open: could not attach PHY: -16", and no oops is
    logged. Binding the driver back and bringing the interface up afterwards
    succeeds with the link up, so the early return leaves the phydev
    reusable. An ordinary bring-up is unaffected, and with the driver left
    unbound the genphy substitution still runs: "PHY [mdio-bus:00] driver
    [Generic PHY]", link up.

 drivers/net/phy/phy_device.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a3..044cefd9840b 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1781,6 +1781,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 			d->driver = &genphy_driver.mdiodrv.driver;
 
 		phydev->is_genphy_driven = 1;
+	} else if (!phydev->drv) {
+		/* d->driver outlives phydev->drv on unbind, precedes it on bind */
+		err = -EBUSY;
+		goto error_put_device;
 	}
 
 	if (!try_module_get(d->driver->owner)) {
-- 
2.53.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: phy: reject attach while the PHY driver is in transition
  2026-09-14 20:42 [PATCH net] net: phy: reject attach while the PHY driver is in transition Aleksei Sviridkin
@ 2026-09-17 11:43 ` netdev-bot+sashiko
  2026-09-17 12:00 ` Maxime Chevallier
  1 sibling, 0 replies; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-09-17 11:43 UTC (permalink / raw)
  To: f
  Cc: netdev, andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni,
	linux-kernel

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:

Critical: 0 · High: 1 · Medium: 1 · Low: 0

- [High] Incomplete fix / residual TOCTOU: the new `else if
  (!phydev->drv)` test in phy_attach_direct()…
- [Medium] Module reference imbalance survives the residual unbind window
  that this patch claims to close.

--

net: phy: reject attach while the PHY driver is in transition

The patch adds an early -EBUSY return in phy_attach_direct() when
d->driver is still set but phydev->drv has already been cleared, which is
the window between phy_remove() and device_unbind_cleanup(). The intent is
to avoid dereferencing a NULL phydev->drv in phy_drv_supports_irq() and to
avoid leaking the PHY driver module reference.

> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 94b2e85e00a37..044cefd9840bb 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -1781,6 +1781,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
>  			d->driver = &genphy_driver.mdiodrv.driver;
>  
>  		phydev->is_genphy_driven = 1;
> +	} else if (!phydev->drv) {
> +		/* d->driver outlives phydev->drv on unbind, precedes it on bind */
> +		err = -EBUSY;
> +		goto error_put_device;
>  	}

[Severity: High]
Is this check able to prevent the oops quoted in the changelog, given that
it samples phydev->drv once and the dereference happens much later in the
same function?

The commit message says the function holds no lock over phydev->drv, and
get_device(d) only pins the device object, so the unbind can land right
after the test:

CPU0 phy_attach_direct()
	} else if (!phydev->drv) {	/* passes, drv still set */

CPU1 phy_remove()
	phydev->drv = NULL;

CPU0 continues in phy_attach_direct()
	if (!phy_drv_supports_irq(phydev->drv) && phy_interrupt_is_valid(phydev))

and phy_drv_supports_irq() has no NULL test:

static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
{
	return phydrv->config_intr && phydrv->handle_interrupt;
}

That is the same faulting access described in the notes (offset 0x128 being
phy_driver::config_intr), and it is the dereference named by the Fixes: tag.
The changelog does acknowledge "an unbind starting mid-attach still races",
but with a Fixes: tag attached, would a stable backporter reasonably
conclude the crash signature is gone when it is still reachable?

Should the single unguarded use be made NULL-tolerant instead, the way
phy_init_hw() already is, rather than adding a sampled check far away from
it?

There is a second dereference in the same window. If
device_unbind_cleanup() also runs, dev->driver becomes NULL:

drivers/base/dd.c:device_unbind_cleanup() {
	...
	device_set_driver(dev, NULL);
	...
}

and the statement immediately after the new check re-reads it:

	if (!try_module_get(d->driver->owner)) {

Can that path fault too, since the new test looks at phydev->drv but this
line dereferences d->driver?

Finally, phylink_bringup_phy() dereferences phy->drv right after a
successful attach. If the unbind stores land just as phy_attach_direct()
returns 0, does that caller hit a NULL phy->drv as well?

[Severity: Medium]
The changelog gives the module reference leak as a reason to fail here:

  "a continued attach would already hold the driver module reference that
  phy_detach() drops only while d->driver is set, leaking it once the
  unbind completes"

Does that leak remain reachable in the window the patch leaves open?
phy_attach_direct() charges the reference keyed on the mutable field, right
after the new check:

	if (!try_module_get(d->driver->owner)) {

and phy_detach() re-reads the same field at release time instead of the
module that was charged:

	if (phydev->mdio.dev.driver)
		module_put(phydev->mdio.dev.driver->owner);

So for the sequence [new check passes] -> [try_module_get(A->owner)] ->
[phy_remove() clears phydev->drv, device_unbind_cleanup() clears
dev->driver] -> [attach fails later, for example on the phydev->attached_dev
"PHY already attached" path, phy_link_topo_add_phy() or phy_init_hw(), or
the netdev is brought down afterwards], phy_detach() sees a NULL
d->driver and skips the module_put(). Does module A then stay pinned
forever?

There is also a mismatched-module variant: if the mdio device is bound to a
different PHY driver B via sysfs before phy_detach() runs, phy_detach()
would call module_put(B->owner) for a reference it never took. Can that
underflow B's refcount and allow unloading a module still in use?

Would recording the module pinned at attach time, rather than sampling
d->driver twice, close both cases?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260914204200.2743251-1-f%40lex.la

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: phy: reject attach while the PHY driver is in transition
  2026-09-14 20:42 [PATCH net] net: phy: reject attach while the PHY driver is in transition Aleksei Sviridkin
  2026-09-17 11:43 ` netdev-bot+sashiko
@ 2026-09-17 12:00 ` Maxime Chevallier
  2026-09-17 18:33   ` Aleksei Sviridkin
  1 sibling, 1 reply; 6+ messages in thread
From: Maxime Chevallier @ 2026-09-17 12:00 UTC (permalink / raw)
  To: Aleksei Sviridkin, netdev
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, linux-kernel

Hi,

On 9/14/26 22:42, Aleksei Sviridkin wrote:
> phy_remove() clears phydev->drv as its last act; the driver core
> clears d->driver only afterwards, in device_unbind_cleanup(). In that
> window phy_attach_direct() skips the genphy substitution, because
> d->driver is still set, and then dereferences the NULL phydev->drv in
> phy_drv_supports_irq().

[...]

>     Verified on an MT7981 board (mtk_eth_soc GMAC, "MediaTek MT7981 PHY" at
>     mdio-bus:00), 6.18.44, with a 200 ms msleep() added at the end of
>     phy_remove() to hold the window open. Two images, identical except for
>     this patch.

If you need to add msleep(200) in the core of the framework to trigger that,
this is net-next material.

Maxime


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: phy: reject attach while the PHY driver is in transition
  2026-09-17 12:00 ` Maxime Chevallier
@ 2026-09-17 18:33   ` Aleksei Sviridkin
  2026-09-17 20:30     ` Maxime Chevallier
  0 siblings, 1 reply; 6+ messages in thread
From: Aleksei Sviridkin @ 2026-09-17 18:33 UTC (permalink / raw)
  To: maxime.chevallier
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, netdev,
	linux-kernel

Fine, v2 will target net-next.

Out of curiosity, since I will run into this again: is the line
"needs instrumentation to reproduce -> net-next" about the window
being unreachable in practice, or about nobody having reported it?
The crash itself came from a real board; the msleep() only made the
race deterministic instead of occasional.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: phy: reject attach while the PHY driver is in transition
  2026-09-17 18:33   ` Aleksei Sviridkin
@ 2026-09-17 20:30     ` Maxime Chevallier
  2026-09-17 21:04       ` Aleksei Sviridkin
  0 siblings, 1 reply; 6+ messages in thread
From: Maxime Chevallier @ 2026-09-17 20:30 UTC (permalink / raw)
  To: Aleksei Sviridkin
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, netdev,
	linux-kernel

Hi,

On 9/17/26 20:33, Aleksei Sviridkin wrote:
> Fine, v2 will target net-next.
> 
> Out of curiosity, since I will run into this again: is the line
> "needs instrumentation to reproduce -> net-next" about the window
> being unreachable in practice, or about nobody having reported it?

Well there are lots of patches recently that fix bugs that are never
seen, and only show-up if some oddball error path is taken, under
conditions almost impossible to encounter in real life or even
reproduce without instrumenting the code to force these conditions.

> The crash itself came from a real board; the msleep() only made the
> race deterministic instead of occasional.

This is something you should say in the commit log then, it's important
to know this was seen in real life. If that's the case, then yes it's
'net' material instead of 'net-next'.

Maxime

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] net: phy: reject attach while the PHY driver is in transition
  2026-09-17 20:30     ` Maxime Chevallier
@ 2026-09-17 21:04       ` Aleksei Sviridkin
  0 siblings, 0 replies; 6+ messages in thread
From: Aleksei Sviridkin @ 2026-09-17 21:04 UTC (permalink / raw)
  To: maxime.chevallier
  Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, netdev,
	linux-kernel

I need to correct what I wrote earlier, because it decides the tree.

What was seen on hardware, with no instrumentation, is the class of
bug: an MT7981 board running an OpenWrt 6.18 kernel, with the distro's
backports and local patches plus the series I was testing on top,
oopsed twice on a NULL phydev->drv after a sysfs unbind of the PHY
driver, once inside a running phy_attach_direct() (in the driver's
config_init) and once in the PHY state machine. I was unbinding on
purpose, racing it against port teardown and bring-up, to stress that
series; none of this shows up in normal operation.

The dereference this patch prevents is a narrower member of that
family: drv already NULL when the attach starts. That window is the
short stretch between phy_remove()'s last store and
device_unbind_cleanup(), and the board never hit it on its own; the
msleep() was needed to reach it at all. So "the msleep() only made the
race deterministic" in my previous mail was wrong. The patch closes the
entry to that window; the wider race, an unbind landing mid-attach, is
untouched.

I will put all of that in the commit log. With both facts on the table,
net with the Fixes tag or net-next is your call.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-17 21:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14 20:42 [PATCH net] net: phy: reject attach while the PHY driver is in transition Aleksei Sviridkin
2026-09-17 11:43 ` netdev-bot+sashiko
2026-09-17 12:00 ` Maxime Chevallier
2026-09-17 18:33   ` Aleksei Sviridkin
2026-09-17 20:30     ` Maxime Chevallier
2026-09-17 21:04       ` Aleksei Sviridkin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®