From: netdev-bot+sashiko@kernel.org
To: suruurism@gmail.com
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
subash.a.kasiviswanathan@oss.qualcomm.com,
sean.tranchetti@oss.qualcomm.com, horms@kernel.org,
kuba@kernel.org
Subject: Re: [PATCH net v3] net: qualcomm: rmnet: require CAP_NET_ADMIN in the real device netns for config ops
Date: Fri, 04 Sep 2026 22:24:39 +0000 [thread overview]
Message-ID: <178856067967.219967.7550358106155720232@kernel.org> (raw)
In-Reply-To: <20260829170656.15548-1-suruurism@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 2
- [Medium] Wrong reference netns in the new capability gate on the
ndo_del_slave path.
- [Low] The two comments added above the new gates in rmnet_add_bridge()
(lines 452-456) and rmnet_del_bridge() (lines 512-516) were copied…
- [Low] The new capability rejections return -EPERM silently.
--
net: qualcomm: rmnet: require CAP_NET_ADMIN in the real device netns for config ops
This adds rtnl_dev_link_net_capable() gates to rmnet_changelink(),
rmnet_add_bridge() and rmnet_del_bridge() so that a caller privileged only
in the rmnet device's netns can no longer rewrite the rmnet port state
attached to a real device living in another netns. rmnet_del_bridge() also
grows local priv/real_dev lookups for the new check.
A few questions below.
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> index bed6f63facf2..6feafc55a7e9 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> @@ -312,6 +312,14 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[],
> if (!rmnet_is_real_dev_registered(real_dev))
> return -ENODEV;
>
> + /*
> + * The rtnl path only checks CAP_NET_ADMIN against dev_net(dev),
> + * but the port state mutated below is attached to real_dev, which
> + * may live in a different netns.
> + */
> + if (!rtnl_dev_link_net_capable(dev, dev_net(real_dev)))
> + return -EPERM;
> +
[Severity: Low]
This isn't a bug, but should the new rejection report through extack?
Both rmnet_changelink() and rmnet_add_bridge() take a
struct netlink_ext_ack *extack, and every other error return in those two
functions fills it in:
NL_SET_ERR_MSG_MOD(extack, "MUX ID already exists");
NL_SET_ERR_MSG_MOD(extack, "Invalid MTU on real dev");
NL_SET_ERR_MSG_MOD(extack, "more than one rmnet dev attached");
NL_SET_ERR_MSG_MOD(extack, "more than one bridge dev attached");
NL_SET_ERR_MSG_MOD(extack, "slave cannot be another rmnet dev");
The closest peer users of the same gate do report the reason, e.g. in
macvlan_changelink():
!rtnl_dev_link_net_capable(dev, dev_net(vlan->lowerdev))) {
NL_SET_ERR_MSG(extack,
"Changing shared macvlan port settings requires CAP_NET_ADMIN in the lower device network namespace");
return -EPERM;
Since the generic rtnetlink capability check also returns -EPERM for
RTM_NEWLINK/RTM_SETLINK, can user space tell the two denials apart as
written?
> port = rmnet_get_port_rtnl(real_dev);
>
> if (data[IFLA_RMNET_MUX_ID]) {
[ ... ]
> @@ -441,6 +449,14 @@ int rmnet_add_bridge(struct net_device *rmnet_dev,
> struct rmnet_port *port, *slave_port;
> int err;
>
> + /*
> + * The rtnl path only checks CAP_NET_ADMIN against dev_net(dev),
> + * but the port state mutated below is attached to real_dev, which
> + * may live in a different netns.
> + */
[Severity: Low]
This isn't a bug, but the comment appears to have been copied from the
rmnet_changelink() hunk without adjusting it.
rmnet_add_bridge() and rmnet_del_bridge() have no dev parameter or local:
int rmnet_add_bridge(struct net_device *rmnet_dev,
struct net_device *slave_dev,
struct netlink_ext_ack *extack)
int rmnet_del_bridge(struct net_device *rmnet_dev,
struct net_device *slave_dev)
The rtnl entry point for these two is RTM_SETLINK IFLA_MASTER via
do_set_master() and ndo_add_slave/ndo_del_slave, where the caller was
checked against dev_net(slave_dev) rather than a changelink on dev. Could
both comments name slave_dev instead of the non-existent dev?
Also, the three added blocks use the form with '/*' alone on the first
line, while the surrounding comments in this file put text on the first
line, e.g. just below in the same function:
/* If there is more than one rmnet dev attached, its probably being
* used for muxing. Skip the briding in that case
*/
> + if (!rtnl_dev_link_net_capable(rmnet_dev, dev_net(real_dev)))
> + return -EPERM;
> +
> port = rmnet_get_port_rtnl(real_dev);
>
> /* If there is more than one rmnet dev attached, its probably being
[ ... ]
> @@ -489,7 +505,19 @@ int rmnet_add_bridge(struct net_device *rmnet_dev,
> int rmnet_del_bridge(struct net_device *rmnet_dev,
> struct net_device *slave_dev)
> {
> - struct rmnet_port *port = rmnet_get_port_rtnl(slave_dev);
> + struct rmnet_priv *priv = netdev_priv(rmnet_dev);
> + struct net_device *real_dev = priv->real_dev;
> + struct rmnet_port *port;
> +
> + /*
> + * The rtnl path only checks CAP_NET_ADMIN against dev_net(dev),
> + * but rmnet_unregister_bridge() below clears the bridge state of
> + * the real device's port, which may live in a different netns.
> + */
> + if (!rtnl_dev_link_net_capable(rmnet_dev, dev_net(real_dev)))
> + return -EPERM;
[Severity: Medium]
Is rmnet_dev the right reference device for the gate on the del path?
rtnl_dev_link_net_capable() short-circuits when the two namespaces are
equal:
net/core/rtnetlink.c:rtnl_dev_link_net_capable() {
return net_eq(link_net, dev_net(dev)) ||
ns_capable(link_net->user_ns, CAP_NET_ADMIN);
}
On ndo_del_slave the netns the rtnl layer actually verified CAP_NET_ADMIN
against is dev_net(slave_dev), and rmnet_dev is simply whatever master is
already linked:
net/core/rtnetlink.c:do_set_master() {
struct net_device *upper_dev = netdev_master_upper_dev_get(dev);
...
if (ops->ndo_del_slave) {
err = ops->ndo_del_slave(upper_dev, dev);
...
}
There is no netns-equality requirement on that master link, and nothing
seems to forbid moving the rmnet vnd device out of the slave's netns:
rmnet_vnd_setup() does not set dev->netns_immutable,
__dev_change_net_namespace() only refuses netns_immutable devices, and
rmnet_config_notify_cb() reacts to NETDEV_UNREGISTER only when
rmnet_is_real_dev_registered(dev) is true, which is false for the vnd
device, so moving rmnet_dev does not tear the bridge down.
If rmnet_dev ends up in real_dev's netns A while slave_dev stays in netns
B, then net_eq(dev_net(real_dev), dev_net(rmnet_dev)) is true, ns_capable()
is never evaluated, and a caller holding CAP_NET_ADMIN only in B can run
"ip link set <slave> nomaster" and reach:
drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c:rmnet_unregister_bridge() {
real_port = rmnet_get_port_rtnl(real_dev);
real_port->bridge_ep = NULL;
real_port->rmnet_mode = RMNET_EPMODE_VND;
}
which is the cross-netns port mutation the commit message describes.
Reaching that state does need a prior CAP_NET_ADMIN-in-A action to move
rmnet_dev, so the window is narrow.
Would using slave_dev as the reference device on both bridge paths close
this? On the add path the two are equivalent, since do_set_master()
resolves the new master with __dev_get_by_index(dev_net(slave_dev),
ifindex), so passing slave_dev there too would make the invariant
explicit.
> +
> + port = rmnet_get_port_rtnl(slave_dev);
>
> rmnet_unregister_bridge(port);
>
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260829170656.15548-1-suruurism%40gmail.com
prev parent reply other threads:[~2026-09-04 22:24 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-29 17:06 [PATCH net v3] net: qualcomm: rmnet: require CAP_NET_ADMIN in the real device netns for config ops user.email
2026-08-31 20:22 ` subash.a.kasiviswanathan
2026-08-31 20:47 ` Suruur
2026-09-01 6:27 ` subash.a.kasiviswanathan
2026-09-04 22:24 ` netdev-bot+sashiko [this message]
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=178856067967.219967.7550358106155720232@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=sean.tranchetti@oss.qualcomm.com \
--cc=subash.a.kasiviswanathan@oss.qualcomm.com \
--cc=suruurism@gmail.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