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

From: Leon Hwang

Date: Thu Aug 20 2026 - 12:20:41 EST


On 2026/8/19 20:22, 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
>
> On x86 this is an int3 in poisoned bpf_prog_pack memory:
>
> Oops: int3: 0000 [#1] SMP NOPTI
> CPU: 18 UID: 0 PID: 94573 Comm: x169 Not tainted 6.18.44 #1 PREEMPT(lazy)
> RIP: 0010:0xffffffffc0601d8d
> Call Trace:
> <TASK>
> ? bpf_trampoline_6442515411+0x1a4/0x21b
> bpf_lsm_bprm_committed_creds+0x5/0x10
> security_bprm_committed_creds+0x5f/0x70
> begin_new_exec+0x2d6/0x410
> ...
>
> We hit this in production on preemptible kernels when progs attached
> through trampolines got detached while their hooks were busy. Adding
> grace periods before the prog free would not help with sleepable progs:
> neither RCU tasks nor RCU tasks trace waits for a task that slept in a
> prog and then got preempted in the gap after it.
>
> Fix it by having the image take a reference on every prog it calls, in
> bpf_tramp_image_alloc(), and drop them in bpf_tramp_image_free(). A
> detached prog now stays loaded until the old image is gone, which
> reverts a deliberate choice of commit e21aa341785c ("bpf: Fix fexit
> trampoline."). Detached fexit progs still stop being called right away
> since the return path is patched.
>
> Fixes: e21aa341785c ("bpf: Fix fexit trampoline.")
> Assisted-by: Claude:unspecified
> Signed-off-by: Florent Revest (Anthropic) <florent.revest@xxxxxxxxx>

lgtm,

Acked-by: Leon Hwang <leon.hwang@xxxxxxxxx>

> [...]