Re: [PATCH bpf v5 3/4] bpf: Fix potential UAF when reading bpf link info

From: Emil Tsalapatis

Date: Tue Jul 21 2026 - 14:56:25 EST


On Tue Jul 21, 2026 at 12:10 AM EDT, Pu Lehui wrote:
> From: Pu Lehui <pulehui@xxxxxxxxxx>
>
> In bpf_link_show_fdinfo and bpf_link_get_info_by_fd, link->prog is
> accessed without holding any locks. If the prog is concurrently replaced
> via bpf_link_update, the old prog can be freed, leading to a potential
> UAF issue.
>
> Before dereferencing the prog, both normal RCU and RCU Tasks Trace read
> locks would normally be required, as BPF_LINK_TYPE_ITER supports both
> non-sleepable and sleepable progs. However, as commit 57b23c0f612d
> ("bpf: Retire rcu_trace_implies_rcu_gp()") clarifies, an RCU Tasks Trace
> grace period implies an RCU grace period, so holding only
> rcu_read_lock() is already sufficient.
>
> Fixes: 0c991ebc8c69 ("bpf: Implement bpf_prog replacement for an active bpf_cgroup_link")
> Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
> Signed-off-by: Pu Lehui <pulehui@xxxxxxxxxx>

Reviewed-by: Emil Tsalapatis <emil@xxxxxxxxxxxxxxx>

> ---
> kernel/bpf/syscall.c | 21 +++++++++++++++++----
> 1 file changed, 17 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 6db306d23b47..cad986807d53 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -3471,9 +3471,10 @@ static const char *bpf_link_type_strs[] = {
> static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
> {
> const struct bpf_link *link = filp->private_data;
> - const struct bpf_prog *prog = link->prog;
> + const struct bpf_prog *prog;
> enum bpf_link_type type = link->type;
> char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
> + u32 prog_id;
>
> if (type < ARRAY_SIZE(bpf_link_type_strs) && bpf_link_type_strs[type]) {
> if (link->type == BPF_LINK_TYPE_KPROBE_MULTI)
> @@ -3490,13 +3491,20 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
> }
> seq_printf(m, "link_id:\t%u\n", link->id);
>
> + rcu_read_lock();
> + prog = READ_ONCE(link->prog);
> if (prog) {
> bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
> + prog_id = prog->aux->id;
> + }
> + rcu_read_unlock();
> +
> + if (prog) {
> seq_printf(m,
> "prog_tag:\t%s\n"
> "prog_id:\t%u\n",
> prog_tag,
> - prog->aux->id);
> + prog_id);
> }
> if (link->ops->show_fdinfo)
> link->ops->show_fdinfo(link, m);
> @@ -5535,6 +5543,7 @@ static int bpf_link_get_info_by_fd(struct file *file,
> {
> struct bpf_link_info __user *uinfo = u64_to_user_ptr(attr->info.info);
> struct bpf_link_info info;
> + const struct bpf_prog *prog;
> u32 info_len = attr->info.info_len;
> int err;
>
> @@ -5549,8 +5558,12 @@ static int bpf_link_get_info_by_fd(struct file *file,
>
> info.type = link->type;
> info.id = link->id;
> - if (link->prog)
> - info.prog_id = link->prog->aux->id;
> +
> + rcu_read_lock();
> + prog = READ_ONCE(link->prog);
> + if (prog)
> + info.prog_id = prog->aux->id;
> + rcu_read_unlock();
>
> if (link->ops->fill_link_info) {
> err = link->ops->fill_link_info(link, &info);