CVE-2025-39915

In the Linux kernel, the following vulnerability has been resolved: net: phy: transfer phy_config_inband() locking responsibility to phylink Problem description =================== Lockdep reports a possible circular locking dependency (AB/BA) between &pl->state_mutex and &phy->lock, as follows. phylink_resolve() // acquires &pl->state_mutex -> phylink_major_config() -> phy_config_inband() // acquires &pl->phydev->lock whereas all the other call sites where &pl->state_mutex and &pl->phydev->lock have the locking scheme reversed. Everywhere else, &pl->phydev->lock is acquired at the top level, and &pl->state_mutex at the lower level. A clear example is phylink_bringup_phy(). The outlier is the newly introduced phy_config_inband() and the existing lock order is the correct one. To understand why it cannot be the other way around, it is sufficient to consider phylink_phy_change(), phylink's callback from the PHY device's phy->phy_link_change() virtual method, invoked by the PHY state machine. phy_link_up() and phy_link_down(), the (indirect) callers of phylink_phy_change(), are called with &phydev->lock acquired. Then phylink_phy_change() acquires its own &pl->state_mutex, to serialize changes made to its pl->phy_state and pl->link_config. So all other instances of &pl->state_mutex and &phydev->lock must be consistent with this order. Problem impact ============== I think the kernel runs a serious deadlock risk if an existing phylink_resolve() thread, which results in a phy_config_inband() call, is concurrent with a phy_link_up() or phy_link_down() call, which will deadlock on &pl->state_mutex in phylink_phy_change(). Practically speaking, the impact may be limited by the slow speed of the medium auto-negotiation protocol, which makes it unlikely for the current state to still be unresolved when a new one is detected, but I think the problem is there. Nonetheless, the problem was discovered using lockdep. Proposed solution ================= Practically speaking, the phy_config_inband() requirement of having phydev->lock acquired must transfer to the caller (phylink is the only caller). There, it must bubble up until immediately before &pl->state_mutex is acquired, for the cases where that takes place. Solution details, considerations, notes ======================================= This is the phy_config_inband() call graph: sfp_upstream_ops :: connect_phy() | v phylink_sfp_connect_phy() | v phylink_sfp_config_phy() | | sfp_upstream_ops :: module_insert() | | | v | phylink_sfp_module_insert() | | | | sfp_upstream_ops :: module_start() | | | | | v | | phylink_sfp_module_start() | | | | v v | phylink_sfp_config_optical() phylink_start() | | | phylink_resume() v v | | phylink_sfp_set_config() | | | v v v phylink_mac_initial_config() | phylink_resolve() | | phylink_ethtool_ksettings_set() v v v phylink_major_config() | v phy_config_inband() phylink_major_config() caller #1, phylink_mac_initial_config(), does not acquire &pl->state_mutex nor do its callers. It must acquire &pl->phydev->lock prior to calling phylink_major_config(). phylink_major_config() caller #2, phylink_resolve() acquires &pl->state_mutex, thus also needs to acquire &pl->phydev->lock. phylink_major_config() caller #3, phylink_ethtool_ksettings_set(), is completely uninteresting, because it only call ---truncated---
Configurations

Configuration 1 (hide)

OR cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*
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:*:*:*:*:*:*

History

19 Oct 2026, 21:10

Type Values Removed Values Added
Summary
  • (es) En el kernel de Linux, la siguiente vulnerabilidad ha sido resuelta: net: phy: transferir la responsabilidad de bloqueo de phy_config_inband() a phylink Descripción del problema =================== Lockdep informa una posible dependencia de bloqueo circular (AB/BA) entre &pl->state_mutex y &phy->lock, como sigue. phylink_resolve() // adquiere &pl->state_mutex -> phylink_major_config() -> phy_config_inband() // adquiere &pl->phydev->lock mientras que todos los demás sitios de llamada donde &pl->state_mutex y &pl->phydev->lock tienen el esquema de bloqueo invertido. En todas partes, &pl->phydev->lock se adquiere en el nivel superior, y &pl->state_mutex en el nivel inferior. Un ejemplo claro es phylink_bringup_phy(). El caso atípico es el recién introducido phy_config_inband() y el orden de bloqueo existente es el correcto. Para entender por qué no puede ser al revés, es suficiente considerar phylink_phy_change(), la devolución de llamada de phylink del método virtual phy->phy_link_change() del dispositivo PHY, invocado por la máquina de estados PHY. phy_link_up() y phy_link_down(), los llamadores (indirectos) de phylink_phy_change(), se llaman con &phydev->lock adquirido. Luego phylink_phy_change() adquiere su propio &pl->state_mutex, para serializar los cambios realizados en su pl->phy_state y pl->link_config. Así que todas las demás instancias de &pl->state_mutex y &phydev->lock deben ser consistentes con este orden. Impacto del problema ============== Creo que el kernel corre un grave riesgo de interbloqueo si un hilo existente de phylink_resolve(), que resulta en una llamada a phy_config_inband(), es concurrente con una llamada a phy_link_up() o phy_link_down(), lo que provocará un interbloqueo en &pl->state_mutex en phylink_phy_change(). Prácticamente hablando, el impacto puede estar limitado por la lenta velocidad del protocolo de auto-negociación del medio, lo que hace improbable que el estado actual siga sin resolverse cuando se detecta uno nuevo, pero creo que el problema existe. No obstante, el problema fue descubierto usando lockdep. Solución propuesta ================= Prácticamente hablando, el requisito de phy_config_inband() de tener phydev->lock adquirido debe transferirse al llamador (phylink es el único llamador). Allí, debe ascender hasta inmediatamente antes de que se adquiera &pl->state_mutex, para los casos en que eso ocurra. Detalles de la solución, consideraciones, notas ======================================= Este es el grafo de llamadas de phy_config_inband(): sfp_upstream_ops :: connect_phy() | v phylink_sfp_connect_phy() | v phylink_sfp_config_phy() | | sfp_upstream_ops :: module_insert() | | | v | phylink_sfp_module_insert() | | | | sfp_upstream_ops :: module_start() | | | | | v | | phylink_sfp_module_start() | | | | v v | phylink_sfp_config_optical() phylink_start() | | | phylink_resume() v v | | phylink_sfp_set_config() | | | v v v phylink_mac_initial_config() | phylink_resolve() | | phylink_ethtool_ksettings_set() v v v phylink_major_config() | v phy_config_inband() El llamador #1 de phylink_major_config(), phylink_mac_initial_config(), no adquiere &pl->state_mutex ni lo hacen sus llamadores. Debe adquirir &pl->phydev->lock antes de llamar a phylink_major_config(). El llamador #2 de --TRUNCADO--

Information

Published : 2025-10-01 08:15

Updated : 2026-10-19 21:10


NVD link : CVE-2025-39915

Mitre link : CVE-2025-39915

CVE.ORG link : CVE-2025-39915


JSON object : View

Products Affected

linux

  • linux_kernel
CWE
CWE-667

Improper Locking