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 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®