From b3c63e77aecb87c6ac6278f55320a18c9ad39eb2 Mon Sep 17 00:00:00 2001 From: Junseo Lim Date: Sat, 15 Aug 2026 16:19:27 +0900 Subject: [PATCH] bpf: fix UAF of trampoline progs before image release arch_prepare_bpf_trampoline() embeds program pointers in the generated image and passes them to __bpf_prog_enter_recur(). After bpf_trampoline_update() replaces the image, bpf_tramp_image_put() can keep the old image executable past a normal RCU grace period. A detached non-sleepable prog can therefore be freed before a preempted task reaches rcu_read_lock_dont_migrate() in __bpf_prog_enter_recur(), leading to a use-after-free. Keep image-local prog refs and drop them from bpf_tramp_image_free(). Fixes: e21aa341785c ("bpf: Fix fexit trampoline.") Reported-by: Sechang Lim Signed-off-by: Junseo Lim --- include/linux/bpf.h | 5 +++++ kernel/bpf/trampoline.c | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 7719f65284456..bacb6bc2e27bd 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1376,6 +1376,11 @@ struct bpf_tramp_image { struct rcu_head rcu; struct work_struct work; }; +#ifdef CONFIG_PREEMPTION + /* Programs called from this image must outlive deferred image freeing. */ + struct bpf_prog *progs[BPF_MAX_TRAMP_LINKS]; + int nr_progs; +#endif }; struct bpf_trampoline { diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 1a721fc4bef56..ff6b331c965b7 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -531,6 +531,13 @@ bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total, bool *ip_a static void bpf_tramp_image_free(struct bpf_tramp_image *im) { +#ifdef CONFIG_PREEMPTION + int i; + + for (i = 0; i < im->nr_progs; i++) + bpf_prog_put(im->progs[i]); +#endif + bpf_image_ksym_del(&im->ksym); arch_free_bpf_trampoline(im->image, im->size); bpf_jit_uncharge_modmem(im->size); @@ -678,6 +685,9 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut u32 orig_flags = tr->flags; bool ip_arg = false; int err, total, size; +#ifdef CONFIG_PREEMPTION + int i, kind; +#endif tnodes = bpf_trampoline_get_progs(tr, &total, &ip_arg); if (IS_ERR(tnodes)) @@ -740,6 +750,16 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut goto out; } +#ifdef CONFIG_PREEMPTION + for (kind = 0; kind < BPF_TRAMP_MAX; kind++) + for (i = 0; i < tnodes[kind].nr_nodes; i++) { + struct bpf_prog *prog = tnodes[kind].nodes[i]->link->prog; + + bpf_prog_inc(prog); + im->progs[im->nr_progs++] = prog; + } +#endif + err = arch_prepare_bpf_trampoline(im, im->image, im->image + size, &tr->func.model, tr->flags, tnodes, tr->func.addr);