net/wireless/nl80211.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
This series addresses two related namespace handling gaps in
nl80211 that came up during a recent inquiry on the list. Both
follow the netdev convention.
Patch 1/2: NL80211_CMD_SET_WIPHY_NETNS uses GENL_UNS_ADMIN_PERM,
which only checks CAP_NET_ADMIN over the source netns. The target
netns selected via NL80211_ATTR_NETNS_FD or NL80211_ATTR_PID is
not checked. Mirror the rtnetlink convention spelled out by
rtnl_get_net_ns_capable() and require ns_capable(target_net->user_ns,
CAP_NET_ADMIN) on the resolved target netns. Reachable from an
unprivileged user namespace whenever the caller already holds a
WIPHY_FLAG_NETNS_OK wiphy in their own netns (mac80211_hwsim, or
an admin-delegated container scenario). PoC reproduces on a KASAN
VM with mac80211_hwsim and shows the call now returns -EPERM.
Patch 2/2: nl80211_prepare_wdev_dump() validates the wdev's netns
on the first dumpit invocation but not on subsequent ones, where
it looks up the wiphy by global index. If the wiphy moves netns
between dumpit invocations (via SET_WIPHY_NETNS), the dump silently
keeps copying BSS list contents from the wiphy's new netns into
the caller's netns. The other dump paths in nl80211.c
(nl80211_dump_wiphy() and the scheduled scan dump) already
re-check on every iteration. Add the same filter to the
continuation path. This is mostly a clarity-and-convention fix.
Combined with 1/2 it also closes the path by which an
unprivileged-userns caller could trigger the race themselves.
Maoyi Xie (2):
wifi: nl80211: require CAP_NET_ADMIN over the target netns in
SET_WIPHY_NETNS
wifi: nl80211: re-check wiphy netns in nl80211_prepare_wdev_dump()
continuation
net/wireless/nl80211.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
base-commit: 65493f27a6008bf84bd11bd41c5e1ea6b0bf3c3d
--
2.34.1
This series addresses two related namespace handling gaps in
nl80211 that came up during a recent inquiry on the list. Both
follow the netdev convention.
Patch 1/2: NL80211_CMD_SET_WIPHY_NETNS uses GENL_UNS_ADMIN_PERM,
which only checks CAP_NET_ADMIN over the source netns. The target
netns selected via NL80211_ATTR_NETNS_FD or NL80211_ATTR_PID is
not checked. Mirror the rtnetlink convention spelled out by
rtnl_get_net_ns_capable() and require ns_capable(target_net->user_ns,
CAP_NET_ADMIN) on the resolved target netns. Reachable from an
unprivileged user namespace whenever the caller already holds a
WIPHY_FLAG_NETNS_OK wiphy in their own netns (mac80211_hwsim, or
an admin-delegated container scenario). PoC reproduces on a KASAN
VM with mac80211_hwsim and shows the call now returns -EPERM.
Patch 2/2: nl80211_prepare_wdev_dump() validates the wdev's netns
on the first dumpit invocation but not on subsequent ones, where
it looks up the wiphy by global index. If the wiphy moves netns
between dumpit invocations (via SET_WIPHY_NETNS), the dump silently
keeps copying BSS list contents from the wiphy's new netns into
the caller's netns. Add a net_eq() check on the continuation path
so the dump terminates cleanly. This is mostly a clarity-and-convention
fix. Combined with 1/2 it also closes the path by which an
unprivileged-userns caller could trigger the race themselves.
Changes since v1:
Patch 2/2: trimmed the comment per Johannes's review. The
comment now describes the invariant being protected (wiphy
may move between dumpit invocations) rather than referencing
other dump callers as motivation.
Patch 1/2: unchanged.
Maoyi Xie (2):
wifi: nl80211: require CAP_NET_ADMIN over the target netns in
SET_WIPHY_NETNS
wifi: nl80211: re-check wiphy netns in nl80211_prepare_wdev_dump()
continuation
net/wireless/nl80211.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
base-commit: 65493f27a6008bf84bd11bd41c5e1ea6b0bf3c3d
--
2.34.1
NL80211_CMD_SET_WIPHY_NETNS dispatches with GENL_UNS_ADMIN_PERM, which
verifies that the caller has CAP_NET_ADMIN over the user namespace
owning the source netns (the netlink socket's netns). It does not
verify that the caller has CAP_NET_ADMIN over the target netns
selected by NL80211_ATTR_NETNS_FD or NL80211_ATTR_PID.
This diverges from the convention enforced in
net/core/rtnetlink.c::rtnl_get_net_ns_capable():
/* For now, the caller is required to have CAP_NET_ADMIN in
* the user namespace owning the target net ns.
*/
if (!sk_ns_capable(sk, net->user_ns, CAP_NET_ADMIN))
return ERR_PTR(-EACCES);
A user with CAP_NET_ADMIN in their own user namespace can therefore
push a wiphy into an arbitrary netns (including init_net) over which
they have no privilege.
Reachable from an unprivileged user namespace as soon as the caller
holds, in their own netns, a wiphy that has WIPHY_FLAG_NETNS_OK set
(true for mac80211_hwsim and for any wiphy that an administrator has
delegated into a container).
Reproducer (mac80211_hwsim, KASAN VM):
1. As real root, modprobe mac80211_hwsim radios=1 in init_net.
2. fork(); child unshare(CLONE_NEWUSER | CLONE_NEWNET) and writes
0-mapped uid_map.
3. Real root migrates phyN into the child's netns via
NL80211_CMD_SET_WIPHY_NETNS (legitimate admin step).
4. Child, with CAP_NET_ADMIN only in its own user_ns, sends
NL80211_CMD_SET_WIPHY_NETNS targeting init_net's netns fd.
5. The kernel honours the request and the wiphy is moved back
to init_net even though the caller has no privilege there.
Mirror the rtnetlink convention by requiring ns_capable(net->user_ns,
CAP_NET_ADMIN) on the resolved target netns before calling
cfg80211_switch_netns().
Reported-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
---
net/wireless/nl80211.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 67088804d..db546dd93 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -13867,6 +13867,19 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(net))
return PTR_ERR(net);
+ /*
+ * The caller already has CAP_NET_ADMIN over the source netns
+ * (enforced by GENL_UNS_ADMIN_PERM on the genl op). Mirror the
+ * convention used by net/core/rtnetlink.c::rtnl_get_net_ns_capable()
+ * and require CAP_NET_ADMIN over the target netns as well, so that
+ * a caller that is privileged in their own user namespace cannot
+ * push a wiphy into a netns where they have no privilege.
+ */
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+ put_net(net);
+ return -EPERM;
+ }
+
err = 0;
/* check if anything to do */
--
2.34.1
On Mon, 2026-05-04 at 21:54 +0800, Maoyi Xie wrote: [...] > Reported-by: Maoyi Xie <maoyi.xie@ntu.edu.sg> Crediting yourself with finding the bug seems ... a bit weird? But anyway > Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg> Bot reports WARNING: From:/Signed-off-by: email address mismatch: 'From: Maoyi Xie <maoyixie.tju@gmail.com>' != 'Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>' so wonder if that was intentional? Maybe you wanted to include another From: line? johannes
Hi Johannes, Thanks for the catch on both. >> Reported-by: Maoyi Xie <maoyi.xie@ntu.edu.sg> > Crediting yourself with finding the bug seems ... a bit weird? The Reported-by line was a mistake. We will drop it in v3. > Bot reports > WARNING: From:/Signed-off-by: email address mismatch: 'From: > Maoyi Xie <maoyixie.tju@gmail.com>' != 'Signed-off-by: Maoyi Xie > <maoyi.xie@ntu.edu.sg>' > so wonder if that was intentional? Maybe you wanted to include > another The From and Signed-off-by mismatch was not intentional. The patch is authored from maoyi.xie@ntu.edu.sg, but our NTU SMTP does not accept git send-email. We sent the series through Gmail with maoyixie.tju@gmail.com as the envelope sender, and Gmail rewrote the From header. In v3 we will set the email header From to the Gmail address and add an explicit From: line at the top of the commit body, with the NTU address, so the trailer matches the author. Maoyi Nanyang Technological University https://maoyixie.com/
NL80211_CMD_GET_SCAN is implemented as a multi-call dumpit. The first
invocation of nl80211_prepare_wdev_dump() validates the requested wdev
against the caller's netns via __cfg80211_wdev_from_attrs(). Subsequent
invocations look up the same wiphy by global index via
wiphy_idx_to_wiphy() and do not re-check that the wiphy is still in
the caller's netns.
If the wiphy is moved between dumpit invocations (via
NL80211_CMD_SET_WIPHY_NETNS), the dump silently continues to copy BSS
list contents from the wiphy's new netns into the caller's netns
socket buffer. The other dump paths in nl80211.c (e.g.
nl80211_dump_wiphy() and the parallel scheduled scan dump) already
filter by net_eq(wiphy_net(...), sock_net(skb->sk)) on every iteration.
Add the same filter to the continuation path. If the wiphy's netns no
longer matches the caller's, return -ENODEV and the netlink dump
machinery terminates the walk cleanly.
This is most usefully fixed alongside the SET_WIPHY_NETNS target-cap
hardening in patch 1/2, which closes the path by which an
unprivileged-userns caller could trigger this race themselves.
Reported-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
---
net/wireless/nl80211.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index db546dd93..f2c91a939 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1276,6 +1276,16 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
rtnl_unlock();
return -ENODEV;
}
+ /*
+ * The first invocation validated the wdev's netns against
+ * the caller via __cfg80211_wdev_from_attrs(). The wiphy
+ * may have moved netns between dumpit invocations (via
+ * NL80211_CMD_SET_WIPHY_NETNS), so re-check here.
+ */
+ if (!net_eq(wiphy_net(wiphy), sock_net(cb->skb->sk))) {
+ rtnl_unlock();
+ return -ENODEV;
+ }
*rdev = wiphy_to_rdev(wiphy);
*wdev = NULL;
--
2.34.1
© 2016 - 2026 Red Hat, Inc.