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