RE: [PATCH net v4] net: qualcomm: rmnet: require CAP_NET_ADMIN in the real device netns for config ops
From: subash.a.kasiviswanathan
Date: Tue Sep 01 2026 - 13:58:38 EST
> -----Original Message-----
> From: Abdifatah Suruur <suruurism@xxxxxxxxx>
> Sent: Tuesday, September 1, 2026 2:15 AM
> To: subash.a.kasiviswanathan@xxxxxxxxxxxxxxxx
> Cc: netdev@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx;
> sean.tranchetti@xxxxxxxxxxxxxxxx; horms@xxxxxxxxxx; Abdifatah Suruur
> <suruurism@xxxxxxxxx>; stable@xxxxxxxxxxxxxxx
> Subject: [PATCH net v4] net: qualcomm: rmnet: require CAP_NET_ADMIN in
> the real device netns for config ops
>
> An rmnet device may be created with its real device in a different netns
than
> the rmnet device itself (rmnet_newlink() resolves it in link_net), and the
rtnl
> config paths below only check CAP_NET_ADMIN against dev_net(dev), while
> mutating rmnet port state attached to the real device:
>
> - rmnet_changelink() rewrites the endpoint mux table and
> port->data_format and, via rmnet_vnd_update_dev_mtu(), can shrink the
> MTU of the rmnet endpoint netdevs.
> - rmnet_add_bridge() and rmnet_del_bridge(), reachable via
> ndo_add_slave/ndo_del_slave through RTM_SETLINK IFLA_MASTER, flip
> port->rmnet_mode and port->bridge_ep on the real device's port; with
> bridge_ep pointing at a caller-owned device, rmnet_rx_handler() then
> forwards real-device ingress frames to it.
>
> A caller privileged only in the rmnet device's netns can therefore rewrite
the
> shared cellular data-path state owned by another netns, and steer its
ingress
> traffic.
>
> Gate all three with rtnl_dev_link_net_capable(), matching the "require
> CAP_NET_ADMIN in the device netns for changelink" series (vxlan/geneve,
> CVE-2026-68432).
>
> Fixes: 2abb5792387e ("net: qualcomm: rmnet: Allow configuration updates
> to existing devices")
> Fixes: 60d58f971c1077 ("net: qualcomm: rmnet: Implement bridge mode")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Abdifatah Suruur <suruurism@xxxxxxxxx>
> ---
> v4:
> - use the netdev comment style, per Subash Abhinav Kasiviswanathan
> v3:
> - cover rmnet_add_bridge() and rmnet_del_bridge() with the same gate;
> they mutate the same real-device port state via ndo_add_slave/
> ndo_del_slave and have no capability check of their own
> - correct the impact wording: rmnet_vnd_update_dev_mtu() only reads
> real_dev->mtu; the MTU store lands on the rmnet endpoint netdevs via
> rmnet_vnd_change_mtu(), not on the real device
> v2:
> - drop Reported-by: (implied for the author), per Jakub Kicinski
> ---
> .../ethernet/qualcomm/rmnet/rmnet_config.c | 27
> ++++++++++++++++++-
> 1 file changed, 26 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> index 78d4df55740a1..73bd5419cb136 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> @@ -312,6 +312,13 @@ 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;
> +
> port = rmnet_get_port_rtnl(real_dev);
>
> if (data[IFLA_RMNET_MUX_ID]) {
> @@ -440,6 +447,13 @@ 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.
> + */
> + 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
> @@ -488,7 +502,18 @@ 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;
> +
> + port = rmnet_get_port_rtnl(slave_dev);
>
> rmnet_unregister_bridge(port);
>
> --
> 2.53.0
Reviewed-by: Subash Abhinov Kasiviswanathan
<subash.a.kasiviswanathan@xxxxxxxxxxxxxxxx>