bpf: failed assertion of infallibility in bpf_tracing_link_release triggers WARNING

1 view
Skip to first unread message

梅开彦

unread,
Nov 14, 2025, 11:08:28 PMNov 14
to b...@vger.kernel.org, dan...@iogearbox.net, ddd...@hust.edu.cn, dz...@hust.edu.cn, a...@kernel.org, hust-os-ker...@googlegroups.com
Our fuzzer discovered that a kernel WARNING can occur when a memory allocation fails during the cleanup of a BPF tracing link, causing the `bpf_trampoline_unlink_prog` function to erroneously report a failure.

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

The kernel warning occurs during the cleanup of a BPF tracing link. When the link is released, the function `bpf_trampoline_get_progs` is called as part of the unlinking process. Inside this function, a `kcalloc` memory allocation is performed.

```c
tlinks = kcalloc(BPF_TRAMP_MAX, sizeof(*tlinks), GFP_KERNEL);
if (!tlinks)
return ERR_PTR(-ENOMEM);
```

The fault injection mechanism intercepts this `kcalloc` operation and forces it to fail, returning a non-zero error code. This error code propagates back up the call stack, violating the assumption that the link separation is infallible, which ultimately triggers the `WARN_ON_ONCE` assertion in `bpf_tracing_link_release`.

## Reproduction Steps

1. **Program Setup**: Load a simple **LSM BPF program** (`BPF_PROG_TYPE_LSM`).
- A valid BTF ID must be acquired (e.g., using `bpftool`) and passed as the `attach_btf_id` parameter to ensure the program is successfully loaded.

2. **Link Creation**: Attach the program using the non-standard **`BPF_RAW_TRACEPOINT_OPEN`** call to obtain a BPF link file descriptor (FD).

3. **Injection**: Enable **Fault Injection** (e.g., `fail-nth`) targeting the subsequent `close()` syscall's memory allocation path.

4. **Trigger**: Execute **`close(FD)`**. The injection forces **`kcalloc`** inside the `bpf_trampoline_get_progs` cleanup path to fail, which triggers the `WARN_ON_ONCE` in **`bpf_tracing_link_release`**.

## Recommended Fix

This rigid assertion is recommended to be removed and replaced with a robust error-handling mechanism. The function shall first validate the return value of `bpf_trampoline_unlink_prog` — errors may arise from scenarios such as `kcalloc` failure. In the event of an error, a non-fatal warning could be logged via `pr_warn_once`, and execution should proceed with subsequent resource cleanup operations. This ensures that no resource leaks occur while maintaining system stability.

## KASAN Report

```
[ 83.154481][ T9911] WARNING: kernel/bpf/syscall.c:3490 at bpf_tracing_link_release+0xf1/0x130, CPU#1: lsm_test_bin/9911
[ 83.154916][ T9911] Modules linked in:
[ 83.155077][ T9911] CPU: 1 UID: 0 PID: 9911 Comm: lsm_test_bin Not tainted 6.18.0-rc5-next-20251111 #6 PREEMPT(full)
[ 83.155505][ T9911] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 83.155869][ T9911] RIP: 0010:bpf_tracing_link_release+0xf1/0x130
[ 83.156125][ T9911] Code: 00 00 00 48 85 ed 74 0d e8 5c 30 e9 ff 48 89 ef e8 04 f8 ff ff 48 83 c4 08 5b 5d 41 5c 41 5d 4
[ 83.156991][ T9911] RSP: 0018:ffffc900032b7d80 EFLAGS: 00010293
[ 83.157237][ T9911] RAX: 0000000000000000 RBX: ffff888102aad500 RCX: ffffffff81d36590
[ 83.157556][ T9911] RDX: ffff888109b31f00 RSI: ffffffff81d36600 RDI: 0000000000000005
[ 83.157872][ T9911] RBP: 00000000fffffff4 R08: 0000000000000001 R09: fffff52000656f91
[ 83.158187][ T9911] R10: 00000000fffffff4 R11: 0000000000000000 R12: ffff888102aad598
[ 83.158504][ T9911] R13: ffff888102aad590 R14: ffffffff81d36480 R15: 0000000002000000
[ 83.158824][ T9911] FS: 00007fd5249e7740(0000) GS:ffff8881a144e000(0000) knlGS:0000000000000000
[ 83.159182][ T9911] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 83.159450][ T9911] CR2: 00005653877452a8 CR3: 000000002dbfb000 CR4: 0000000000752ef0
[ 83.159768][ T9911] PKRU: 55555554
[ 83.159915][ T9911] Call Trace:
[ 83.160052][ T9911] <TASK>
[ 83.160175][ T9911] bpf_link_free+0xf0/0x390
[ 83.160365][ T9911] bpf_link_release+0x61/0x80
[ 83.160560][ T9911] __fput+0x407/0xb50
[ 83.160730][ T9911] fput_close_sync+0x114/0x210
[ 83.160928][ T9911] ? __pfx_fput_close_sync+0x10/0x10
[ 83.161145][ T9911] ? dnotify_flush+0x7e/0x4c0
[ 83.161345][ T9911] __x64_sys_close+0x93/0x120
[ 83.161539][ T9911] do_syscall_64+0xcb/0xfa0
[ 83.161728][ T9911] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 83.161970][ T9911] RIP: 0033:0x7fd524aeb7d9
[ 83.162152][ T9911] Code: 08 89 e8 5b 5d c3 66 2e 0f 1f 84 00 00 00 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 8
[ 83.162919][ T9911] RSP: 002b:00007ffde5f7ce18 EFLAGS: 00000217 ORIG_RAX: 0000000000000003
[ 83.163256][ T9911] RAX: ffffffffffffffda RBX: 00007ffde5f7cf68 RCX: 00007fd524aeb7d9
[ 83.163574][ T9911] RDX: 00007fd524ae29a0 RSI: 0000000000000001 RDI: 0000000000000004
[ 83.163893][ T9911] RBP: 00007ffde5f7ce50 R08: 0000000000000064 R09: 0000000000000018
[ 83.164211][ T9911] R10: 0000000000000000 R11: 0000000000000217 R12: 0000000000000000
[ 83.164529][ T9911] R13: 00007ffde5f7cf78 R14: 000056536c351dd8 R15: 00007fd524c0c020
[ 83.164855][ T9911] </TASK>
[ 83.164982][ T9911] Kernel panic - not syncing: kernel: panic_on_warn set ...
[ 83.165276][ T9911] CPU: 1 UID: 0 PID: 9911 Comm: lsm_test_bin Not tainted 6.18.0-rc5-next-20251111 #6 PREEMPT(full)
[ 83.165704][ T9911] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 83.166070][ T9911] Call Trace:
[ 83.166205][ T9911] <TASK>
[ 83.166329][ T9911] dump_stack_lvl+0x3d/0x1b0
[ 83.166522][ T9911] vpanic+0x67e/0x710
[ 83.166690][ T9911] ? bpf_tracing_link_release+0xf1/0x130
[ 83.166921][ T9911] panic+0xc7/0xd0
[ 83.167078][ T9911] ? __pfx_panic+0x10/0x10
[ 83.167267][ T9911] ? check_panic_on_warn+0x24/0xc0
[ 83.167478][ T9911] check_panic_on_warn+0xb6/0xc0
[ 83.167682][ T9911] __warn+0x10d/0x3f0
[ 83.167848][ T9911] ? bpf_tracing_link_release+0xf1/0x130
[ 83.168079][ T9911] report_bug+0x2e1/0x500
[ 83.168260][ T9911] ? bpf_tracing_link_release+0xf1/0x130
[ 83.168493][ T9911] handle_bug+0x2dd/0x410
[ 83.168674][ T9911] ? __pfx___mutex_unlock_slowpath+0x10/0x10
[ 83.168920][ T9911] exc_invalid_op+0x35/0x80
[ 83.169111][ T9911] asm_exc_invalid_op+0x1a/0x20
[ 83.169311][ T9911] RIP: 0010:bpf_tracing_link_release+0xf1/0x130
[ 83.169565][ T9911] Code: 00 00 00 48 85 ed 74 0d e8 5c 30 e9 ff 48 89 ef e8 04 f8 ff ff 48 83 c4 08 5b 5d 41 5c 41 5d 4
[ 83.170332][ T9911] RSP: 0018:ffffc900032b7d80 EFLAGS: 00010293
[ 83.170579][ T9911] RAX: 0000000000000000 RBX: ffff888102aad500 RCX: ffffffff81d36590
[ 83.170895][ T9911] RDX: ffff888109b31f00 RSI: ffffffff81d36600 RDI: 0000000000000005
[ 83.171214][ T9911] RBP: 00000000fffffff4 R08: 0000000000000001 R09: fffff52000656f91
[ 83.171532][ T9911] R10: 00000000fffffff4 R11: 0000000000000000 R12: ffff888102aad598
[ 83.171848][ T9911] R13: ffff888102aad590 R14: ffffffff81d36480 R15: 0000000002000000
[ 83.172166][ T9911] ? __pfx_bpf_link_release+0x10/0x10
[ 83.172392][ T9911] ? bpf_tracing_link_release+0x80/0x130
[ 83.172621][ T9911] ? bpf_tracing_link_release+0xf0/0x130
[ 83.172854][ T9911] bpf_link_free+0xf0/0x390
[ 83.173041][ T9911] bpf_link_release+0x61/0x80
[ 83.173234][ T9911] __fput+0x407/0xb50
[ 83.173404][ T9911] fput_close_sync+0x114/0x210
[ 83.173600][ T9911] ? __pfx_fput_close_sync+0x10/0x10
[ 83.173817][ T9911] ? dnotify_flush+0x7e/0x4c0
[ 83.174015][ T9911] __x64_sys_close+0x93/0x120
[ 83.174211][ T9911] do_syscall_64+0xcb/0xfa0
[ 83.174401][ T9911] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 83.174643][ T9911] RIP: 0033:0x7fd524aeb7d9
[ 83.174825][ T9911] Code: 08 89 e8 5b 5d c3 66 2e 0f 1f 84 00 00 00 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 8
[ 83.175582][ T9911] RSP: 002b:00007ffde5f7ce18 EFLAGS: 00000217 ORIG_RAX: 0000000000000003
[ 83.175918][ T9911] RAX: ffffffffffffffda RBX: 00007ffde5f7cf68 RCX: 00007fd524aeb7d9
[ 83.176236][ T9911] RDX: 00007fd524ae29a0 RSI: 0000000000000001 RDI: 0000000000000004
[ 83.176554][ T9911] RBP: 00007ffde5f7ce50 R08: 0000000000000064 R09: 0000000000000018
[ 83.176871][ T9911] R10: 0000000000000000 R11: 0000000000000217 R12: 0000000000000000
[ 83.177185][ T9911] R13: 00007ffde5f7cf78 R14: 000056536c351dd8 R15: 00007fd524c0c020
[ 83.177510][ T9911] </TASK>
```

## Proof of Concept

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

```c
#!/bin/bash

SOURCE_FILE="lsm_test_temp.c"
OUTPUT_FILE="lsm_test_bin"
BTF_FILE="/sys/kernel/btf/vmlinux"
SYMBOL_NAME="bpf_lsm_file_mprotect"

if ! command -v bpftool &> /dev/null; then
echo "Error: 'bpftool' command not found. Attempting to install via apt..."
sudo apt update && sudo apt install -y bpftool
if [ $? -ne 0 ]; then
echo "Error: Failed to install bpftool using apt. Please install manually."
exit 1
fi
echo "bpftool installed successfully."
fi

if [ ! -f "$BTF_FILE" ]; then
echo "Error: $BTF_FILE (Kernel BTF information) not found. Ensure your kernel supports BTF."
exit 1
fi

echo "--- 1. Searching for BTF ID of '$SYMBOL_NAME' in $BTF_FILE ---"

BTF_ID=$(bpftool btf dump file $BTF_FILE 2>/dev/null | grep "FUNC.*$SYMBOL_NAME" | awk '{print $1}' | tr -d '[]')

if [ -z "$BTF_ID" ] || [ "$BTF_ID" -eq 0 ]; then
echo "Error: Could not extract a valid BTF ID for '$SYMBOL_NAME'."
exit 1
fi

echo "Found BTF ID: $BTF_ID"
echo "------------------------------------------------------"

echo "--- 2. Generating C source file ($SOURCE_FILE) ---"

cat << EOF > $SOURCE_FILE
#define _GNU_SOURCE

#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/bpf.h>

#define ATTACH_BTF_ID $BTF_ID

#ifndef __NR_bpf
#define __NR_bpf 321
#endif

#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
((struct bpf_insn) { \
.code = BPF_LD | BPF_DW | BPF_IMM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = (__u32) (IMM) }), \
((struct bpf_insn) { \
.code = 0, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = ((__u64) (IMM)) >> 32 })

#define BPF_LD_IMM64(DST, IMM) \
BPF_LD_IMM64_RAW(DST, 0, IMM)

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


static int inject_fault()
{
int failslab_fd = open("/sys/kernel/debug/failslab/ignore-gfp-wait", O_WRONLY);
if (failslab_fd >= 0) {
write(failslab_fd, "N", 1);
close(failslab_fd);
printf("[+] Set ignore-gfp-wait=N\n");
}

int fail_fd = open("/proc/self/fail-nth", O_WRONLY);
if (fail_fd >= 0) {
write(fail_fd, "3", 1);
close(fail_fd);
}
return 0;
}


uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};

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_LSM,
.insns = ptr_to_u64(insns),
.insn_cnt = cnt,
.license = ptr_to_u64("GPL"),
.attach_btf_id = ATTACH_BTF_ID,
.expected_attach_type = BPF_LSM_MAC,
};
printf("[i] Attempting to load BPF LSM program, BTF ID: %u\n", ATTACH_BTF_ID);
int prog_fd=syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
return prog_fd;
}

static int trace_point_open(int fd){

union bpf_attr attr = {
.raw_tracepoint={
.prog_fd=fd,
}
};
int trace_fd=syscall(__NR_bpf, BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
return trace_fd;
}


int main(void)
{
intptr_t res = 0;
struct bpf_insn prog[] = {
BPF_LD_IMM64(BPF_REG_0, 0x0),
BPF_EXIT_INSN()
};

int fd=load_prog(prog, sizeof(prog) / sizeof(prog[0]));
if (fd < 0) {
perror("Failed to load BPF program");
return 1;
}
printf("BPF program loaded successfully, fd: %d\n", fd);

res = trace_point_open(fd);
if (res != -1) {
r[1] = res;
printf("Tracepoint opened successfully, fd: %d\n", (int)res);
} else {
perror("Failed to open tracepoint (Note: LSM programs usually don't use this call)");
}

inject_fault();

if (r[1] != 0xffffffffffffffff) {
syscall(__NR_close, /*fd=*/r[1]);
printf("Closed tracepoint file descriptor: %d\n", (int)r[1]);
}

syscall(__NR_close, fd);
printf("Closed BPF program file descriptor: %d\n", fd);

return 0;
}
EOF

gcc -Wall -g -o $OUTPUT_FILE $SOURCE_FILE

./$OUTPUT_FILE

rm -f $SOURCE_FILE $OUTPUT_FILE

```

## Kernel Configuration Requirements for Reproduction

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

config-20251111
Reply all
Reply to author
Forward
0 new messages