CVE-2025-39915
Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
01/10/2025
Last modified:
14/01/2026
Description
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
net: phy: transfer phy_config_inband() locking responsibility to phylink<br />
<br />
Problem description<br />
===================<br />
<br />
Lockdep reports a possible circular locking dependency (AB/BA) between<br />
&pl->state_mutex and &phy->lock, as follows.<br />
<br />
phylink_resolve() // acquires &pl->state_mutex<br />
-> phylink_major_config()<br />
-> phy_config_inband() // acquires &pl->phydev->lock<br />
<br />
whereas all the other call sites where &pl->state_mutex and<br />
&pl->phydev->lock have the locking scheme reversed. Everywhere else,<br />
&pl->phydev->lock is acquired at the top level, and &pl->state_mutex at<br />
the lower level. A clear example is phylink_bringup_phy().<br />
<br />
The outlier is the newly introduced phy_config_inband() and the existing<br />
lock order is the correct one. To understand why it cannot be the other<br />
way around, it is sufficient to consider phylink_phy_change(), phylink&#39;s<br />
callback from the PHY device&#39;s phy->phy_link_change() virtual method,<br />
invoked by the PHY state machine.<br />
<br />
phy_link_up() and phy_link_down(), the (indirect) callers of<br />
phylink_phy_change(), are called with &phydev->lock acquired.<br />
Then phylink_phy_change() acquires its own &pl->state_mutex, to<br />
serialize changes made to its pl->phy_state and pl->link_config.<br />
So all other instances of &pl->state_mutex and &phydev->lock must be<br />
consistent with this order.<br />
<br />
Problem impact<br />
==============<br />
<br />
I think the kernel runs a serious deadlock risk if an existing<br />
phylink_resolve() thread, which results in a phy_config_inband() call,<br />
is concurrent with a phy_link_up() or phy_link_down() call, which will<br />
deadlock on &pl->state_mutex in phylink_phy_change(). Practically<br />
speaking, the impact may be limited by the slow speed of the medium<br />
auto-negotiation protocol, which makes it unlikely for the current state<br />
to still be unresolved when a new one is detected, but I think the<br />
problem is there. Nonetheless, the problem was discovered using lockdep.<br />
<br />
Proposed solution<br />
=================<br />
<br />
Practically speaking, the phy_config_inband() requirement of having<br />
phydev->lock acquired must transfer to the caller (phylink is the only<br />
caller). There, it must bubble up until immediately before<br />
&pl->state_mutex is acquired, for the cases where that takes place.<br />
<br />
Solution details, considerations, notes<br />
=======================================<br />
<br />
This is the phy_config_inband() call graph:<br />
<br />
sfp_upstream_ops :: connect_phy()<br />
|<br />
v<br />
phylink_sfp_connect_phy()<br />
|<br />
v<br />
phylink_sfp_config_phy()<br />
|<br />
| sfp_upstream_ops :: module_insert()<br />
| |<br />
| v<br />
| phylink_sfp_module_insert()<br />
| |<br />
| | sfp_upstream_ops :: module_start()<br />
| | |<br />
| | v<br />
| | phylink_sfp_module_start()<br />
| | |<br />
| v v<br />
| phylink_sfp_config_optical()<br />
phylink_start() | |<br />
| phylink_resume() v v<br />
| | phylink_sfp_set_config()<br />
| | |<br />
v v v<br />
phylink_mac_initial_config()<br />
| phylink_resolve()<br />
| | phylink_ethtool_ksettings_set()<br />
v v v<br />
phylink_major_config()<br />
|<br />
v<br />
phy_config_inband()<br />
<br />
phylink_major_config() caller #1, phylink_mac_initial_config(), does not<br />
acquire &pl->state_mutex nor do its callers. It must acquire<br />
&pl->phydev->lock prior to calling phylink_major_config().<br />
<br />
phylink_major_config() caller #2, phylink_resolve() acquires<br />
&pl->state_mutex, thus also needs to acquire &pl->phydev->lock.<br />
<br />
phylink_major_config() caller #3, phylink_ethtool_ksettings_set(), is<br />
completely uninteresting, because it only call<br />
---truncated---
Impact
Base Score 3.x
5.50
Severity 3.x
MEDIUM
Vulnerable products and versions
| CPE | From | Up to |
|---|---|---|
| cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* | 6.14 (including) | 6.16.8 (excluding) |
| cpe:2.3:o:linux:linux_kernel:6.17:rc1:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:6.17:rc2:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:6.17:rc3:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:6.17:rc4:*:*:*:*:*:* | ||
| cpe:2.3:o:linux:linux_kernel:6.17:rc5:*:*:*:*:*:* |
To consult the complete list of CPE names with products and versions, see this page



