Re: [PATCH bpf v6 3/4] bpf: Fix potential UAF when reading bpf link info
From: Andrii Nakryiko
Date: Mon Jul 27 2026 - 19:16:40 EST
On Wed, Jul 22, 2026 at 12:19 AM Pu Lehui <pulehui@xxxxxxxxxxxxxxx> 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
no, either one is enough (and that's why we wait for RCU and RCU Tasks
Trace grace periods, to make sure that both kinds of accesses work).
This paragraph is misleading, just drop it.
> 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>
> Reviewed-by: Emil Tsalapatis <emil@xxxxxxxxxxxxxxx>
> Reviewed-by: Amery Hung <ameryhung@xxxxxxxxx>
> Signed-off-by: Pu Lehui <pulehui@xxxxxxxxxx>
> ---
> 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;
can be read uninitialized (if link->prog is null), initialize to zero
pw-bot: cr
>
> 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);
> --
> 2.34.1
>