From: netdev-bot+sashiko@kernel.org
To: maxime.chevallier@bootlin.com
Cc: davem@davemloft.net, andrew@lunn.ch, kuba@kernel.org,
edumazet@google.com, pabeni@redhat.com, linux@armlinux.org.uk,
hkallweit1@gmail.com, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, thomas.petazzoni@bootlin.com,
christophe.leroy@csgroup.eu, herve.codina@bootlin.com,
f.fainelli@gmail.com, vladimir.oltean@nxp.com,
kory.maincent@bootlin.com, kabel@kernel.org,
o.rempel@pengutronix.de, nicveronese@gmail.com, horms@kernel.org,
mwojtas@chromium.org, romain.gantois@bootlin.com,
daniel@makrotopia.org, dimitri.fedrau@liebherr.com,
frank.wunderlich@linux.dev, p.ameruoso@live.it
Subject: Re: [PATCH RESEND net-next v17 07/10] net: phy: phy_link_topology: Add a helper to retrieve ports
Date: Fri, 11 Sep 2026 18:52:09 +0000 [thread overview]
Message-ID: <178915272984.219967.7179871656200305196@kernel.org> (raw)
In-Reply-To: <20260910170103.1029108-8-maxime.chevallier@bootlin.com>
Thank you for your contribution! Sashiko AI review found 5 potential
issue(s) to consider:
Critical: 0 · High: 2 · Medium: 2 (1) · Low: 1
- [High] NULL pointer dereference newly introduced in
phy_link_topo_add_port(): the added `if…
- [High] phy_link_topo_get_port() hands out a raw, unreferenced `struct
phy_port *` and documents RTNL as the sole lifetime guarantee ("Caller…
- [Medium] The ops-locked guard added to phy_link_topo_add_port() is
sampled too early to establish the invariant it exists for.
- [Medium] The commit message ("net: phy: phy_link_topology: Add a helper
to retrieve ports") describes only the addition of the read helper,…
- [Low] Reported for completeness, assessed as not currently a bug:
phy_link_topo_add_port() turns the ops-locked case into a hard failure…
Pre-existing issues:
- [Medium] Reported for completeness, assessed as a pre-existing pattern
with unproven reachability: phy_link_topo_get_port() loads…
--
net: phy: phy_link_topology: Add a helper to retrieve ports
This adds phy_link_topo_get_port(), an inline helper that looks up a
phy_port in a netdev's link topology xarray under rtnl, and adds an
ops-locked guard to phy_link_topo_add_port().
> In order to allow netlink access to phy_ports, let's add a helper to
> retrieve them. When handling a port coming from phy_link_topology, the
> caller must hold rtnl until it's done with it.
[Severity: Medium]
The message describes only the new read helper, but the patch also changes
the behaviour of the existing exported write path. Should the message
mention that phy_link_topo_add_port() now refuses registration with
-EOPNOTSUPP (plus a WARN splat) for ops-locked devices?
All callers turn that into a hard failure:
phylink_create_sfp_cage_port() -> phylink_register_sfp() -> phylink_create()
phylink_add_sfp_mod_port() (SFP module insertion)
phy_add_sfp_mod_port() / phy_attach_direct() (PHY attach)
The new getter has no callers in this patch, so the add_port() hunk is the
only behaviour-affecting change here, which makes it easy to misread the
patch as an inert helper addition.
> diff --git a/drivers/net/phy/phy_link_topology.c b/drivers/net/phy/phy_link_topology.c
> index 28c5bf3a8849a..22885bf2e0455 100644
> --- a/drivers/net/phy/phy_link_topology.c
> +++ b/drivers/net/phy/phy_link_topology.c
> @@ -65,6 +65,14 @@ int phy_link_topo_add_port(struct net_device *dev, struct phy_port *port)
> struct phy_link_topology *topo;
> int ret;
>
> + /* Ports can now be queried without rtnl for ops-locked devices, which
> + * we don't support now as port retrieval is done under rtnl.
> + * We don't have phy_port enabled devices yet, let's make sure
> + * we are loudly warned about that when it happens.
> + */
> + if (WARN_ON_ONCE(netdev_need_ops_lock(dev)))
> + return -EOPNOTSUPP;
> +
[Severity: High]
Can this dereference a NULL dev->netdev_ops during probe? With
CONFIG_NET_SHAPER=y, netdev_need_ops_lock() in include/net/netdev_lock.h
does:
bool ret = dev->request_ops_lock || !!dev->queue_mgmt_ops;
#if IS_ENABLED(CONFIG_NET_SHAPER)
ret |= !!dev->netdev_ops->net_shaper_ops;
#endif
phy_link_topo_add_port() is reachable from probe via
phylink_create() -> phylink_register_sfp() -> phylink_create_sfp_cage_port()
-> phy_link_topo_add_port()
and some drivers call phylink_create() before installing netdev_ops. In
mtk_add_mac() the phylink_create() call comes first and
"eth->netdev[id]->netdev_ops = &mtk_netdev_ops;" is only executed further
down; the netdev comes from alloc_etherdev_mqs() and net/core does not
install a default netdev_ops, so dev->netdev_ops is still NULL at the time
of the new check. MediaTek boards with "sfp = <&sfpN>" on the eth-mac node
exist in tree (mt7986a-bananapi-bpi-r3.dts, mt7988a-bananapi-bpi-r4.dts),
so pl->sfp_bus is non-NULL and the cage port path is taken.
Note the identical pre-existing check in phy_link_topo_add_phy() is only
reached from phy_attach_direct(), i.e. after netdev_ops is set, so this is
new exposure for the port path.
[Severity: Medium]
Related to the above ordering: is this guard sampled early enough to be
meaningful for the SFP cage port?
For the cage port the check runs inside phylink_create(), before drivers
populate the very fields netdev_need_ops_lock() inspects
(dev->request_ops_lock, dev->queue_mgmt_ops,
dev->netdev_ops->net_shaper_ops). mtk_add_mac() again shows the ordering:
phylink_create() first, netdev_ops assignment afterwards, with the netdev
freshly zeroed by alloc_etherdev_mqs().
A driver that becomes ops-locked after phylink_create() would therefore
register ports with no warning at all, which leaves exactly the
rtnl-versus-netdev_lock combination that the new
phy_link_topo_get_port() ASSERT_RTNL() assumes has been excluded. Would
checking at a later point, or asserting in phy_link_topo_get_port()'s
caller path, catch that case?
[Severity: Low]
This isn't a bug today, but is the hard -EOPNOTSUPP the intended
granularity? Every caller propagates it as fatal (probe failure via
phylink_create(), or a failed SFP module insertion / PHY attach), whereas
simply declining to register the port (return 0, leave it out of the
xarray) would be enough for an rtnl-only getter.
For what it is worth, no in-tree ops-locked driver hits this yet: among the
drivers with queue_mgmt_ops / request_ops_lock / net_shaper_ops only fbnic
uses phylink, and fbnic_phylink_create() passes fwnode == NULL, so
phylink_register_sfp() returns early and the cage port is never created.
> topo = phy_link_topo_get_or_alloc(dev);
> if (IS_ERR(topo))
> return PTR_ERR(topo);
> diff --git a/include/linux/phy_link_topology.h b/include/linux/phy_link_topology.h
> index 296ee514ba463..9585282cae3fc 100644
> --- a/include/linux/phy_link_topology.h
> +++ b/include/linux/phy_link_topology.h
[ ... ]
> @@ -71,6 +72,25 @@ phy_link_topo_get_phy(struct net_device *dev, u32 phyindex)
> return NULL;
> }
>
> +static inline struct phy_port *
> +phy_link_topo_get_port(struct net_device *dev, u32 port_id)
> +{
> + struct phy_link_topology *topo = dev->link_topo;
> +
> + if (!topo)
> + return NULL;
> +
[Severity: Medium]
This is a pre-existing pattern rather than something introduced here
(phy_link_topo_get_phy() reads dev->link_topo the same way, and
netdev_alloc_phy_link_topology() is untouched), but the plain load of
dev->link_topo happens before ASSERT_RTNL(), and the NULL path skips the
assertion entirely.
The publishing side has no release semantics either:
drivers/net/phy/phy_link_topology.c:netdev_alloc_phy_link_topology() {
xa_init_flags(&topo->ports, XA_FLAGS_ALLOC1);
topo->next_port_index = 1;
dev->link_topo = topo;
}
and phy_link_topo_get_or_alloc() documents that this can run "from
.probe() : No rtnl, no netdev_lock". In practice register_netdevice()
takes rtnl after the probe-time allocation, which appears to order the
xarray init against any rtnl-holding reader, so I could not construct a
reachable interleaving. Would publishing dev->link_topo with release
semantics and asserting rtnl before touching it be worth doing anyway?
> + /* ports in the topo are RTNL protected, but may be accessed under
> + * netdev_lock for ops-locked devices. For now keep them under rtnl
> + * protection, as no ops-locked devices have phy_port capabilities
> + * yet.
> + */
> + ASSERT_RTNL();
> +
> + /* Caller must hold RTNL while handling the phy_port */
> + return xa_load(&topo->ports, port_id);
> +}
[Severity: High]
Is rtnl really sufficient to keep the returned phy_port alive? The helper
hands out a raw pointer with no reference taken, but the write side of
topo->ports neither takes nor asserts rtnl, and phy_port_destroy() is a
bare kfree() with no kref and no RCU deferral:
drivers/net/phy/phy_port.c:phy_port_destroy() {
kfree(port);
}
phy_link_topo_del_port() has no lock assertion either:
drivers/net/phy/phy_link_topology.c:phy_link_topo_del_port() {
struct phy_link_topology *topo = dev->link_topo;
if (!topo)
return;
xa_erase(&topo->ports, port->id);
}
Two free paths appear to run without rtnl. On PHY driver unbind:
phy_remove() -> phy_cleanup_ports() {
list_for_each_entry_safe(port, tmp, &phydev->ports, head) {
phy_del_port(phydev, port);
phy_port_destroy(port);
}
}
and phylink teardown, whose kerneldoc states the opposite of the getter's
contract:
drivers/net/phy/phylink.c
* Note: the rtnl lock must not be held when calling this function.
*/
void phylink_destroy(struct phylink *pl)
phylink_destroy_sfp_cage_port() {
if (pl->netdev && pl->sfp_cage_port)
phy_link_topo_del_port(pl->netdev, pl->sfp_cage_port);
if (pl->sfp_cage_port)
phy_port_destroy(pl->sfp_cage_port);
}
xa_erase() stops new lookups but gives no grace period, so a reader that
already obtained the pointer under rtnl can dereference freed memory.
Later in this series net/ethtool/port.c does exactly that:
port = phy_link_topo_get_port(req_info->dev, request->port_id);
if (!port)
return -ENODEV;
linkmode_copy(reply->supported, port->supported);
Would a kref on phy_port (or kfree_rcu, or taking and asserting the same
lock on every add/del/destroy path) close this? For reference, the SFP
state machine paths do look safe here: drivers/net/phy/sfp.c takes
rtnl_lock() around sfp_sm_event(), so the module-port add/del are
serialized; it is the probe/unbind/destroy paths above that are not.
> +
> #else
> static inline int phy_link_topo_add_phy(struct net_device *dev,
> struct phy_device *phy,
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910170103.1029108-1-maxime.chevallier%40bootlin.com
next prev parent reply other threads:[~2026-09-11 18:52 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 17:00 [PATCH RESEND net-next v17 00/10] net: phy_port: SFP modules representation and phy_port listing Maxime Chevallier
2026-09-10 17:00 ` [PATCH RESEND net-next v17 01/10] net: phy: phy_link_topology: Add a helper for opportunistic alloc Maxime Chevallier
2026-09-11 18:51 ` netdev-bot+sashiko
2026-09-10 17:00 ` [PATCH RESEND net-next v17 02/10] net: phy: phy_link_topology: Track ports in phy_link_topology Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko
2026-09-10 17:00 ` [PATCH RESEND net-next v17 03/10] net: phylink: Register a phy_port for MAC-driven SFP cages Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko
2026-09-10 17:00 ` [PATCH RESEND net-next v17 04/10] net: phy: Create SFP phy_port before registering upstream Maxime Chevallier
2026-09-10 17:00 ` [PATCH RESEND net-next v17 05/10] net: phy: Represent PHY-less SFP modules with phy_port Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko
2026-09-10 17:00 ` [PATCH RESEND net-next v17 06/10] net: phy: phy_port: Store information about a port's upstream Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko
2026-09-10 17:00 ` [PATCH RESEND net-next v17 07/10] net: phy: phy_link_topology: Add a helper to retrieve ports Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko [this message]
2026-09-10 17:01 ` [PATCH RESEND net-next v17 08/10] netlink: specs: Add ethernet port listing with ethtool Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko
2026-09-10 17:01 ` [PATCH RESEND net-next v17 09/10] net: ethtool: Introduce ethtool command to list ports Maxime Chevallier
2026-09-11 18:52 ` netdev-bot+sashiko
2026-09-10 17:01 ` [PATCH RESEND net-next v17 10/10] Documentation: networking: Update the phy_port infrastructure description Maxime Chevallier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178915272984.219967.7179871656200305196@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew@lunn.ch \
--cc=christophe.leroy@csgroup.eu \
--cc=daniel@makrotopia.org \
--cc=davem@davemloft.net \
--cc=dimitri.fedrau@liebherr.com \
--cc=edumazet@google.com \
--cc=f.fainelli@gmail.com \
--cc=frank.wunderlich@linux.dev \
--cc=herve.codina@bootlin.com \
--cc=hkallweit1@gmail.com \
--cc=horms@kernel.org \
--cc=kabel@kernel.org \
--cc=kory.maincent@bootlin.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=maxime.chevallier@bootlin.com \
--cc=mwojtas@chromium.org \
--cc=netdev@vger.kernel.org \
--cc=nicveronese@gmail.com \
--cc=o.rempel@pengutronix.de \
--cc=p.ameruoso@live.it \
--cc=pabeni@redhat.com \
--cc=romain.gantois@bootlin.com \
--cc=thomas.petazzoni@bootlin.com \
--cc=vladimir.oltean@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.