Alongside previous patch, the infinite loop issue caused by combination of tailcal and freplace can be prevented completely. The previous patch can not prevent the use case that updates a prog to prog_array map and then extends the prog with freplace prog. This patch fixes the case by preventing extending a prog, which has been updated to prog_array map, with freplace prog. If a prog has been updated to prog_array map, it or its subprog can not be extended by freplace prog. Signed-off-by: Leon Hwang <leon.hwang@xxxxxxxxx> --- include/linux/bpf.h | 1 + kernel/bpf/arraymap.c | 6 +++++- kernel/bpf/syscall.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 048aa2625cbef..b864b37e67c17 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1484,6 +1484,7 @@ struct bpf_prog_aux { bool exception_cb; bool exception_boundary; bool is_extended; /* true if extended by freplace program */ + atomic_t tail_callee_cnt; struct bpf_arena *arena; /* BTF_KIND_FUNC_PROTO for valid attach_btf_id */ const struct btf_type *attach_func_proto; diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 8d97bae98fa70..c12e0e3bf6ad0 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -961,13 +961,17 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map, return ERR_PTR(-EINVAL); } + atomic_inc(&prog->aux->tail_callee_cnt); return prog; } static void prog_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer) { + struct bpf_prog *prog = ptr; + /* bpf_prog is freed after one RCU or tasks trace grace period */ - bpf_prog_put(ptr); + atomic_dec(&prog->aux->tail_callee_cnt); + bpf_prog_put(prog); } static u32 prog_fd_array_sys_lookup_elem(void *ptr) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 18b3f9216b050..be829016d8182 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3501,6 +3501,18 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog, tgt_prog = prog->aux->dst_prog; } + if (prog->type == BPF_PROG_TYPE_EXT && + atomic_read(&tgt_prog->aux->tail_callee_cnt)) { + /* Program extensions can not extend target prog when the target + * prog has been updated to any prog_array map as tail callee. + * It's to prevent a potential infinite loop like: + * tgt prog entry -> tgt prog subprog -> freplace prog entry + * --tailcall-> tgt prog entry. + */ + err = -EINVAL; + goto out_unlock; + } + err = bpf_link_prime(&link->link.link, &link_primer); if (err) goto out_unlock; -- 2.44.0