[PATCH bpf v2 0/2] bpf: Fix use-after-free of progs detached from busy trampolines

From: Florent Revest (Anthropic)

Date: Sat Sep 12 2026 - 06:00:47 EST


A task running in a trampoline image can call a prog that was detached
and freed in the meantime, when it slept in a sleepable prog before
reaching the detached one or was preempted between two progs. Patch 2
adds a selftest that demonstrates this and patch 1 contains the fix.

v1 kept the progs alive by having the image hold a reference on them
until it is freed, which Alexei pointed out pins detached progs for as
long as a task sleeps in the traced function. v2 follows his suggestion
to extend the ip_after_call nop patching instead: every prog call in the
image is preceded by a nop, and detaching a prog patches its nop into a
jump over the call in the images that are still around. Nothing is
pinned and nothing changes on the fast path besides the nop.

One thing is a bit different from what we discussed: instead of
patching all the nops to jump to the epilogue when the image is put,
only the nop of the detached prog is patched, when it is detached, and
it jumps over just that prog. Patching all of them would make tasks that
are in an old image skip fentry and fmod_ret progs that are still
attached whenever the trampoline is updated (e.g. an LSM prog missing a
check because another prog got attached to the same hook), and those
can't jump to the epilogue anyway since the original function still has
to be called. This needs the trampoline to know which of its images are
still in use, which is the list added here.

The nops also cost a bit of room in the image: 38 progs no longer fit in
a page on arm64 and loongarch, so BPF_MAX_TRAMP_LINKS is lowered there
(and on powerpc, where 38 already didn't fit) the way s390 does it. The
alternative would be to let images span two pages, I can do that
instead if it is preferred.

Tested on x86_64 under KVM and on arm64, s390x, powerpc64le, riscv64
and loongarch64 under qemu TCG, all with KASAN: the new selftest crashes
the unpatched kernel on every one of them and passes with the series,
and trampoline_count, fentry/fexit, modify_return and test_lsm pass too
(the full test_progs on x86_64). The trig-fentry/fexit/fmodret
benchmarks on x86_64 are within noise.

Changes since v1
(https://lore.kernel.org/bpf/20260819122252.1782790-1-florent.revest@xxxxxxxxx/):
- Patch nops in front of detached progs instead of taking prog
references from the image (Alexei)
- Lower BPF_MAX_TRAMP_LINKS on arm64, loongarch and powerpc so the
image still fits in a page
- Explain that the sleepable case doesn't depend on CONFIG_PREEMPTION
(Kumar, Alexei)
- Add a selftest (Jiri, Alexei)
- Add Sechang's Reported-by (Junseo, Kumar)
- Drop Leon's and Kumar's acks since the code changed entirely

Florent Revest (Anthropic) (2):
bpf: Skip detached progs in trampoline images that are still in use
selftests/bpf: Detach a trampoline prog while a task sleeps before it

arch/arm64/net/bpf_jit_comp.c | 29 ++-
arch/loongarch/net/bpf_jit.c | 35 ++--
arch/powerpc/net/bpf_jit_comp.c | 38 ++--
arch/riscv/net/bpf_jit_comp64.c | 34 ++--
arch/s390/net/bpf_jit_comp.c | 35 +++-
arch/x86/net/bpf_jit_comp.c | 23 ++-
include/linux/bpf.h | 40 +++-
kernel/bpf/trampoline.c | 51 ++++-
.../bpf/prog_tests/tramp_prog_detach.c | 191 ++++++++++++++++++
.../selftests/bpf/progs/tramp_prog_detach.c | 56 +++++
10 files changed, 465 insertions(+), 67 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c
create mode 100644 tools/testing/selftests/bpf/progs/tramp_prog_detach.c


base-commit: 15071f2a1263e82150c77eeb1e94dbfc31950a8e
--
2.55.0