Skip to content

Commit 987aa73

Browse files
Asphalttgregkh
authored andcommitted
bpf: Prevent tailcall infinite loop caused by freplace
[ Upstream commit d6083f0 ] There is a potential infinite loop issue that can occur when using a combination of tail calls and freplace. In an upcoming selftest, the attach target for entry_freplace of tailcall_freplace.c is subprog_tc of tc_bpf2bpf.c, while the tail call in entry_freplace leads to entry_tc. This results in an infinite loop: entry_tc -> subprog_tc -> entry_freplace --tailcall-> entry_tc. The problem arises because the tail_call_cnt in entry_freplace resets to zero each time entry_freplace is executed, causing the tail call mechanism to never terminate, eventually leading to a kernel panic. To fix this issue, the solution is twofold: 1. Prevent updating a program extended by an freplace program to a prog_array map. 2. Prevent extending a program that is already part of a prog_array map with an freplace program. This ensures that: * If a program or its subprogram has been extended by an freplace program, it can no longer be updated to a prog_array map. * If a program has been added to a prog_array map, neither it nor its subprograms can be extended by an freplace program. Moreover, an extension program should not be tailcalled. As such, return -EINVAL if the program has a type of BPF_PROG_TYPE_EXT when adding it to a prog_array map. Additionally, fix a minor code style issue by replacing eight spaces with a tab for proper formatting. Reviewed-by: Eduard Zingerman <[email protected]> Signed-off-by: Leon Hwang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 5b6209c commit 987aa73

File tree

5 files changed

+81
-17
lines changed

5 files changed

+81
-17
lines changed

include/linux/bpf.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,8 +1300,12 @@ void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
13001300
bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr);
13011301

13021302
#ifdef CONFIG_BPF_JIT
1303-
int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
1304-
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
1303+
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
1304+
struct bpf_trampoline *tr,
1305+
struct bpf_prog *tgt_prog);
1306+
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
1307+
struct bpf_trampoline *tr,
1308+
struct bpf_prog *tgt_prog);
13051309
struct bpf_trampoline *bpf_trampoline_get(u64 key,
13061310
struct bpf_attach_target_info *tgt_info);
13071311
void bpf_trampoline_put(struct bpf_trampoline *tr);
@@ -1383,12 +1387,14 @@ void bpf_jit_uncharge_modmem(u32 size);
13831387
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
13841388
#else
13851389
static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
1386-
struct bpf_trampoline *tr)
1390+
struct bpf_trampoline *tr,
1391+
struct bpf_prog *tgt_prog)
13871392
{
13881393
return -ENOTSUPP;
13891394
}
13901395
static inline int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
1391-
struct bpf_trampoline *tr)
1396+
struct bpf_trampoline *tr,
1397+
struct bpf_prog *tgt_prog)
13921398
{
13931399
return -ENOTSUPP;
13941400
}
@@ -1492,6 +1498,9 @@ struct bpf_prog_aux {
14921498
bool xdp_has_frags;
14931499
bool exception_cb;
14941500
bool exception_boundary;
1501+
bool is_extended; /* true if extended by freplace program */
1502+
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
1503+
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
14951504
struct bpf_arena *arena;
14961505
/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
14971506
const struct btf_type *attach_func_proto;

kernel/bpf/arraymap.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -947,22 +947,44 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
947947
struct file *map_file, int fd)
948948
{
949949
struct bpf_prog *prog = bpf_prog_get(fd);
950+
bool is_extended;
950951

951952
if (IS_ERR(prog))
952953
return prog;
953954

954-
if (!bpf_prog_map_compatible(map, prog)) {
955+
if (prog->type == BPF_PROG_TYPE_EXT ||
956+
!bpf_prog_map_compatible(map, prog)) {
955957
bpf_prog_put(prog);
956958
return ERR_PTR(-EINVAL);
957959
}
958960

961+
mutex_lock(&prog->aux->ext_mutex);
962+
is_extended = prog->aux->is_extended;
963+
if (!is_extended)
964+
prog->aux->prog_array_member_cnt++;
965+
mutex_unlock(&prog->aux->ext_mutex);
966+
if (is_extended) {
967+
/* Extended prog can not be tail callee. It's to prevent a
968+
* potential infinite loop like:
969+
* tail callee prog entry -> tail callee prog subprog ->
970+
* freplace prog entry --tailcall-> tail callee prog entry.
971+
*/
972+
bpf_prog_put(prog);
973+
return ERR_PTR(-EBUSY);
974+
}
975+
959976
return prog;
960977
}
961978

962979
static void prog_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
963980
{
981+
struct bpf_prog *prog = ptr;
982+
983+
mutex_lock(&prog->aux->ext_mutex);
984+
prog->aux->prog_array_member_cnt--;
985+
mutex_unlock(&prog->aux->ext_mutex);
964986
/* bpf_prog is freed after one RCU or tasks trace grace period */
965-
bpf_prog_put(ptr);
987+
bpf_prog_put(prog);
966988
}
967989

968990
static u32 prog_fd_array_sys_lookup_elem(void *ptr)

kernel/bpf/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
131131
INIT_LIST_HEAD_RCU(&fp->aux->ksym_prefix.lnode);
132132
#endif
133133
mutex_init(&fp->aux->used_maps_mutex);
134+
mutex_init(&fp->aux->ext_mutex);
134135
mutex_init(&fp->aux->dst_mutex);
135136

136137
return fp;

kernel/bpf/syscall.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3218,7 +3218,8 @@ static void bpf_tracing_link_release(struct bpf_link *link)
32183218
container_of(link, struct bpf_tracing_link, link.link);
32193219

32203220
WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
3221-
tr_link->trampoline));
3221+
tr_link->trampoline,
3222+
tr_link->tgt_prog));
32223223

32233224
bpf_trampoline_put(tr_link->trampoline);
32243225

@@ -3358,7 +3359,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
33583359
* in prog->aux
33593360
*
33603361
* - if prog->aux->dst_trampoline is NULL, the program has already been
3361-
* attached to a target and its initial target was cleared (below)
3362+
* attached to a target and its initial target was cleared (below)
33623363
*
33633364
* - if tgt_prog != NULL, the caller specified tgt_prog_fd +
33643365
* target_btf_id using the link_create API.
@@ -3433,7 +3434,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
34333434
if (err)
34343435
goto out_unlock;
34353436

3436-
err = bpf_trampoline_link_prog(&link->link, tr);
3437+
err = bpf_trampoline_link_prog(&link->link, tr, tgt_prog);
34373438
if (err) {
34383439
bpf_link_cleanup(&link_primer);
34393440
link = NULL;

kernel/bpf/trampoline.c

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,27 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
528528
}
529529
}
530530

531-
static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
531+
static int bpf_freplace_check_tgt_prog(struct bpf_prog *tgt_prog)
532+
{
533+
struct bpf_prog_aux *aux = tgt_prog->aux;
534+
535+
guard(mutex)(&aux->ext_mutex);
536+
if (aux->prog_array_member_cnt)
537+
/* Program extensions can not extend target prog when the target
538+
* prog has been updated to any prog_array map as tail callee.
539+
* It's to prevent a potential infinite loop like:
540+
* tgt prog entry -> tgt prog subprog -> freplace prog entry
541+
* --tailcall-> tgt prog entry.
542+
*/
543+
return -EBUSY;
544+
545+
aux->is_extended = true;
546+
return 0;
547+
}
548+
549+
static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
550+
struct bpf_trampoline *tr,
551+
struct bpf_prog *tgt_prog)
532552
{
533553
enum bpf_tramp_prog_type kind;
534554
struct bpf_tramp_link *link_exiting;
@@ -549,6 +569,9 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
549569
/* Cannot attach extension if fentry/fexit are in use. */
550570
if (cnt)
551571
return -EBUSY;
572+
err = bpf_freplace_check_tgt_prog(tgt_prog);
573+
if (err)
574+
return err;
552575
tr->extension_prog = link->link.prog;
553576
return bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP, NULL,
554577
link->link.prog->bpf_func);
@@ -575,17 +598,21 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
575598
return err;
576599
}
577600

578-
int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
601+
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
602+
struct bpf_trampoline *tr,
603+
struct bpf_prog *tgt_prog)
579604
{
580605
int err;
581606

582607
mutex_lock(&tr->mutex);
583-
err = __bpf_trampoline_link_prog(link, tr);
608+
err = __bpf_trampoline_link_prog(link, tr, tgt_prog);
584609
mutex_unlock(&tr->mutex);
585610
return err;
586611
}
587612

588-
static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
613+
static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
614+
struct bpf_trampoline *tr,
615+
struct bpf_prog *tgt_prog)
589616
{
590617
enum bpf_tramp_prog_type kind;
591618
int err;
@@ -596,6 +623,8 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_
596623
err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP,
597624
tr->extension_prog->bpf_func, NULL);
598625
tr->extension_prog = NULL;
626+
guard(mutex)(&tgt_prog->aux->ext_mutex);
627+
tgt_prog->aux->is_extended = false;
599628
return err;
600629
}
601630
hlist_del_init(&link->tramp_hlist);
@@ -604,12 +633,14 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_
604633
}
605634

606635
/* bpf_trampoline_unlink_prog() should never fail. */
607-
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
636+
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
637+
struct bpf_trampoline *tr,
638+
struct bpf_prog *tgt_prog)
608639
{
609640
int err;
610641

611642
mutex_lock(&tr->mutex);
612-
err = __bpf_trampoline_unlink_prog(link, tr);
643+
err = __bpf_trampoline_unlink_prog(link, tr, tgt_prog);
613644
mutex_unlock(&tr->mutex);
614645
return err;
615646
}
@@ -624,7 +655,7 @@ static void bpf_shim_tramp_link_release(struct bpf_link *link)
624655
if (!shim_link->trampoline)
625656
return;
626657

627-
WARN_ON_ONCE(bpf_trampoline_unlink_prog(&shim_link->link, shim_link->trampoline));
658+
WARN_ON_ONCE(bpf_trampoline_unlink_prog(&shim_link->link, shim_link->trampoline, NULL));
628659
bpf_trampoline_put(shim_link->trampoline);
629660
}
630661

@@ -738,7 +769,7 @@ int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,
738769
goto err;
739770
}
740771

741-
err = __bpf_trampoline_link_prog(&shim_link->link, tr);
772+
err = __bpf_trampoline_link_prog(&shim_link->link, tr, NULL);
742773
if (err)
743774
goto err;
744775

0 commit comments

Comments
 (0)