Re: [PATCH bpf] bpf: Keep progs alive until the trampoline image calling them is freed

From: Florent Revest

Date: Mon Aug 31 2026 - 18:55:14 EST


On Mon Aug 31, 2026 at 4:40 PM UTC, Jiri Olsa wrote:
> On Wed, Aug 19, 2026 at 12:22:50PM +0000, Florent Revest (Anthropic) wrote:
> > bpf_tramp_image_put() makes sure a trampoline image is not freed while
> > a task may still be running in it (call_rcu_tasks() + im->pcref), but
> > nothing similar is done for the progs called by that image. Since
> > commit e21aa341785c ("bpf: Fix fexit trampoline."), detach patches the
> > return path so that a task still in the original function skips the
> > fexit progs when it comes back, and counts on the prog's own RCU flavor
> > to cover a task that is inside a prog. On that basis the last prog
> > reference is dropped right away and the prog is freed after a single
> > RCU / RCU tasks trace grace period.
> >
> > That leaves out a task in the trampoline glue itself: between two
> > progs, or already past the patched jump but not yet in the first fexit
> > prog's enter helper. On !PREEMPT kernels this is a few instructions
> > that cannot be preempted, so it did not matter. With CONFIG_PREEMPTION
> > a task can sit there, in no RCU read section of any flavor and holding
> > only im->pcref, for longer than it takes to free the prog it is about
> > to call:
> >
> > CPU 0 CPU 1
> > in image I, orig_call() returned
> > [preempted before lsm.s prog A]
> > bpf_tracing_link_release()
> > -> bpf_tramp_image_put(I)
> > bpf_link_dealloc()
> > bpf_prog_put(A), last ref
> > tasks trace GP, A's text freed
> > __bpf_prog_enter_sleepable(A)
> > call A->bpf_func
>
> hi,
> do you have a selftest or another reproducer for this?

I originally only reproduced that with a stress test (an attach/detach
loop during an exec storm).

But actually, preemption isn't needed! If a task sleeps in a sleepable
prog and the prog that runs after it in the same image gets detached,
it calls the freed prog when it wakes up. That case could be made
deterministic with userfaultfd so I could write a selftest for it.
I'll include it in v2. :)