Skip to content

Commit 8ca77b8

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-fix-tailcall-infinite-loop-caused-by-freplace'
Leon Hwang says: ==================== bpf: Fix tailcall infinite loop caused by freplace Previously, I addressed a tailcall infinite loop issue related to trampolines[0]. In this patchset, I resolve a similar issue where a tailcall infinite loop can occur due to the combination of tailcalls and freplace programs. The fix prevents adding extended programs to the prog_array map and blocks the extension of a tail callee program with freplace. Key changes: 1. If a program or its subprogram has been extended by an freplace program, it can no longer be updated to a prog_array map. 2. If a program has been added to a prog_array map, neither it nor its subprograms can be extended by an freplace program. Additionally, an extension program should not be tailcalled. As a result, return -EINVAL if the program has a type of BPF_PROG_TYPE_EXT when adding it to a prog_array map. Changes: v7 -> v8: * Address comment from Alexei: * guard(mutex) should not hold range all the way through bpf_arch_text_poke(). * Address suggestion from Xu Kuohai: * Extension prog should not be tailcalled independently. v6 -> v7: * Address comments from Alexei: * Rewrite commit message more imperative and consice with AI. * Extend bpf_trampoline_link_prog() and bpf_trampoline_unlink_prog() to link and unlink target prog for freplace prog. * Use guard(mutex)(&tgt_prog->aux->ext_mutex) instead of mutex_lock()&mutex_unlock() pair. * Address comment from Eduard: * Remove misplaced "Reported-by" and "Closes" tags. v5 -> v6: * Fix a build warning reported by kernel test robot. v4 -> v5: * Move code of linking/unlinking target prog of freplace to trampoline.c. * Address comments from Alexei: * Change type of prog_array_member_cnt to u64. * Combine two patches to one. v3 -> v4: * Address comments from Eduard: * Rename 'tail_callee_cnt' to 'prog_array_member_cnt'. * Add comment to 'prog_array_member_cnt'. * Use a mutex to protect 'is_extended' and 'prog_array_member_cnt'. v2 -> v3: * Address comments from Alexei: * Stop hacking JIT. * Prevent the specific use case at attach/update time. v1 -> v2: * Address comment from Eduard: * Explain why nop5 and xor/nop3 are swapped at prologue. * Address comment from Alexei: * Disallow attaching tail_call_reachable freplace prog to not-tail_call_reachable target in verifier. * Update "bpf, arm64: Fix tailcall infinite loop caused by freplace" with latest arm64 JIT code. Links: [0] https://lore.kernel.org/bpf/[email protected]/ ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents f987a64 + 021611d commit 8ca77b8

File tree

7 files changed

+190
-33
lines changed

7 files changed

+190
-33
lines changed

include/linux/bpf.h

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

12941294
#ifdef CONFIG_BPF_JIT
1295-
int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
1296-
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
1295+
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
1296+
struct bpf_trampoline *tr,
1297+
struct bpf_prog *tgt_prog);
1298+
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
1299+
struct bpf_trampoline *tr,
1300+
struct bpf_prog *tgt_prog);
12971301
struct bpf_trampoline *bpf_trampoline_get(u64 key,
12981302
struct bpf_attach_target_info *tgt_info);
12991303
void bpf_trampoline_put(struct bpf_trampoline *tr);
@@ -1374,12 +1378,14 @@ void bpf_jit_uncharge_modmem(u32 size);
13741378
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
13751379
#else
13761380
static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
1377-
struct bpf_trampoline *tr)
1381+
struct bpf_trampoline *tr,
1382+
struct bpf_prog *tgt_prog)
13781383
{
13791384
return -ENOTSUPP;
13801385
}
13811386
static inline int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
1382-
struct bpf_trampoline *tr)
1387+
struct bpf_trampoline *tr,
1388+
struct bpf_prog *tgt_prog)
13831389
{
13841390
return -ENOTSUPP;
13851391
}
@@ -1483,6 +1489,9 @@ struct bpf_prog_aux {
14831489
bool xdp_has_frags;
14841490
bool exception_cb;
14851491
bool exception_boundary;
1492+
bool is_extended; /* true if extended by freplace program */
1493+
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
1494+
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
14861495
struct bpf_arena *arena;
14871496
/* BTF_KIND_FUNC_PROTO for valid attach_btf_id */
14881497
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
@@ -3214,7 +3214,8 @@ static void bpf_tracing_link_release(struct bpf_link *link)
32143214
container_of(link, struct bpf_tracing_link, link.link);
32153215

32163216
WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link,
3217-
tr_link->trampoline));
3217+
tr_link->trampoline,
3218+
tr_link->tgt_prog));
32183219

32193220
bpf_trampoline_put(tr_link->trampoline);
32203221

@@ -3354,7 +3355,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
33543355
* in prog->aux
33553356
*
33563357
* - if prog->aux->dst_trampoline is NULL, the program has already been
3357-
* attached to a target and its initial target was cleared (below)
3358+
* attached to a target and its initial target was cleared (below)
33583359
*
33593360
* - if tgt_prog != NULL, the caller specified tgt_prog_fd +
33603361
* target_btf_id using the link_create API.
@@ -3429,7 +3430,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
34293430
if (err)
34303431
goto out_unlock;
34313432

3432-
err = bpf_trampoline_link_prog(&link->link, tr);
3433+
err = bpf_trampoline_link_prog(&link->link, tr, tgt_prog);
34333434
if (err) {
34343435
bpf_link_cleanup(&link_primer);
34353436
link = NULL;

kernel/bpf/trampoline.c

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,27 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
523523
}
524524
}
525525

526-
static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
526+
static int bpf_freplace_check_tgt_prog(struct bpf_prog *tgt_prog)
527+
{
528+
struct bpf_prog_aux *aux = tgt_prog->aux;
529+
530+
guard(mutex)(&aux->ext_mutex);
531+
if (aux->prog_array_member_cnt)
532+
/* Program extensions can not extend target prog when the target
533+
* prog has been updated to any prog_array map as tail callee.
534+
* It's to prevent a potential infinite loop like:
535+
* tgt prog entry -> tgt prog subprog -> freplace prog entry
536+
* --tailcall-> tgt prog entry.
537+
*/
538+
return -EBUSY;
539+
540+
aux->is_extended = true;
541+
return 0;
542+
}
543+
544+
static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link,
545+
struct bpf_trampoline *tr,
546+
struct bpf_prog *tgt_prog)
527547
{
528548
enum bpf_tramp_prog_type kind;
529549
struct bpf_tramp_link *link_exiting;
@@ -544,6 +564,9 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
544564
/* Cannot attach extension if fentry/fexit are in use. */
545565
if (cnt)
546566
return -EBUSY;
567+
err = bpf_freplace_check_tgt_prog(tgt_prog);
568+
if (err)
569+
return err;
547570
tr->extension_prog = link->link.prog;
548571
return bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP, NULL,
549572
link->link.prog->bpf_func);
@@ -570,17 +593,21 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr
570593
return err;
571594
}
572595

573-
int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
596+
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
597+
struct bpf_trampoline *tr,
598+
struct bpf_prog *tgt_prog)
574599
{
575600
int err;
576601

577602
mutex_lock(&tr->mutex);
578-
err = __bpf_trampoline_link_prog(link, tr);
603+
err = __bpf_trampoline_link_prog(link, tr, tgt_prog);
579604
mutex_unlock(&tr->mutex);
580605
return err;
581606
}
582607

583-
static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
608+
static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
609+
struct bpf_trampoline *tr,
610+
struct bpf_prog *tgt_prog)
584611
{
585612
enum bpf_tramp_prog_type kind;
586613
int err;
@@ -591,6 +618,8 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_
591618
err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP,
592619
tr->extension_prog->bpf_func, NULL);
593620
tr->extension_prog = NULL;
621+
guard(mutex)(&tgt_prog->aux->ext_mutex);
622+
tgt_prog->aux->is_extended = false;
594623
return err;
595624
}
596625
hlist_del_init(&link->tramp_hlist);
@@ -599,12 +628,14 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_
599628
}
600629

601630
/* bpf_trampoline_unlink_prog() should never fail. */
602-
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr)
631+
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
632+
struct bpf_trampoline *tr,
633+
struct bpf_prog *tgt_prog)
603634
{
604635
int err;
605636

606637
mutex_lock(&tr->mutex);
607-
err = __bpf_trampoline_unlink_prog(link, tr);
638+
err = __bpf_trampoline_unlink_prog(link, tr, tgt_prog);
608639
mutex_unlock(&tr->mutex);
609640
return err;
610641
}
@@ -619,7 +650,7 @@ static void bpf_shim_tramp_link_release(struct bpf_link *link)
619650
if (!shim_link->trampoline)
620651
return;
621652

622-
WARN_ON_ONCE(bpf_trampoline_unlink_prog(&shim_link->link, shim_link->trampoline));
653+
WARN_ON_ONCE(bpf_trampoline_unlink_prog(&shim_link->link, shim_link->trampoline, NULL));
623654
bpf_trampoline_put(shim_link->trampoline);
624655
}
625656

@@ -733,7 +764,7 @@ int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,
733764
goto err;
734765
}
735766

736-
err = __bpf_trampoline_link_prog(&shim_link->link, tr);
767+
err = __bpf_trampoline_link_prog(&shim_link->link, tr, NULL);
737768
if (err)
738769
goto err;
739770

tools/testing/selftests/bpf/prog_tests/tailcalls.c

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,16 +1496,16 @@ static void test_tailcall_bpf2bpf_hierarchy_3(void)
14961496
RUN_TESTS(tailcall_bpf2bpf_hierarchy3);
14971497
}
14981498

1499-
/* test_tailcall_freplace checks that the attached freplace prog is OK to
1500-
* update the prog_array map.
1499+
/* test_tailcall_freplace checks that the freplace prog fails to update the
1500+
* prog_array map, no matter whether the freplace prog attaches to its target.
15011501
*/
15021502
static void test_tailcall_freplace(void)
15031503
{
15041504
struct tailcall_freplace *freplace_skel = NULL;
15051505
struct bpf_link *freplace_link = NULL;
15061506
struct bpf_program *freplace_prog;
15071507
struct tc_bpf2bpf *tc_skel = NULL;
1508-
int prog_fd, map_fd;
1508+
int prog_fd, tc_prog_fd, map_fd;
15091509
char buff[128] = {};
15101510
int err, key;
15111511

@@ -1523,37 +1523,127 @@ static void test_tailcall_freplace(void)
15231523
if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load"))
15241524
goto out;
15251525

1526-
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
1526+
tc_prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
15271527
freplace_prog = freplace_skel->progs.entry_freplace;
1528-
err = bpf_program__set_attach_target(freplace_prog, prog_fd, "subprog");
1528+
err = bpf_program__set_attach_target(freplace_prog, tc_prog_fd,
1529+
"subprog_tc");
15291530
if (!ASSERT_OK(err, "set_attach_target"))
15301531
goto out;
15311532

15321533
err = tailcall_freplace__load(freplace_skel);
15331534
if (!ASSERT_OK(err, "tailcall_freplace__load"))
15341535
goto out;
15351536

1536-
freplace_link = bpf_program__attach_freplace(freplace_prog, prog_fd,
1537-
"subprog");
1537+
map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
1538+
prog_fd = bpf_program__fd(freplace_prog);
1539+
key = 0;
1540+
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
1541+
ASSERT_ERR(err, "update jmp_table failure");
1542+
1543+
freplace_link = bpf_program__attach_freplace(freplace_prog, tc_prog_fd,
1544+
"subprog_tc");
15381545
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
15391546
goto out;
15401547

1541-
map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
1542-
prog_fd = bpf_program__fd(freplace_prog);
1548+
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
1549+
ASSERT_ERR(err, "update jmp_table failure");
1550+
1551+
out:
1552+
bpf_link__destroy(freplace_link);
1553+
tailcall_freplace__destroy(freplace_skel);
1554+
tc_bpf2bpf__destroy(tc_skel);
1555+
}
1556+
1557+
/* test_tailcall_bpf2bpf_freplace checks the failure that fails to attach a tail
1558+
* callee prog with freplace prog or fails to update an extended prog to
1559+
* prog_array map.
1560+
*/
1561+
static void test_tailcall_bpf2bpf_freplace(void)
1562+
{
1563+
struct tailcall_freplace *freplace_skel = NULL;
1564+
struct bpf_link *freplace_link = NULL;
1565+
struct tc_bpf2bpf *tc_skel = NULL;
1566+
char buff[128] = {};
1567+
int prog_fd, map_fd;
1568+
int err, key;
1569+
1570+
LIBBPF_OPTS(bpf_test_run_opts, topts,
1571+
.data_in = buff,
1572+
.data_size_in = sizeof(buff),
1573+
.repeat = 1,
1574+
);
1575+
1576+
tc_skel = tc_bpf2bpf__open_and_load();
1577+
if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load"))
1578+
goto out;
1579+
1580+
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
1581+
freplace_skel = tailcall_freplace__open();
1582+
if (!ASSERT_OK_PTR(freplace_skel, "tailcall_freplace__open"))
1583+
goto out;
1584+
1585+
err = bpf_program__set_attach_target(freplace_skel->progs.entry_freplace,
1586+
prog_fd, "subprog_tc");
1587+
if (!ASSERT_OK(err, "set_attach_target"))
1588+
goto out;
1589+
1590+
err = tailcall_freplace__load(freplace_skel);
1591+
if (!ASSERT_OK(err, "tailcall_freplace__load"))
1592+
goto out;
1593+
1594+
/* OK to attach then detach freplace prog. */
1595+
1596+
freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
1597+
prog_fd, "subprog_tc");
1598+
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
1599+
goto out;
1600+
1601+
err = bpf_link__destroy(freplace_link);
1602+
if (!ASSERT_OK(err, "destroy link"))
1603+
goto out;
1604+
1605+
/* OK to update prog_array map then delete element from the map. */
1606+
15431607
key = 0;
1608+
map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
15441609
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
15451610
if (!ASSERT_OK(err, "update jmp_table"))
15461611
goto out;
15471612

1548-
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
1549-
err = bpf_prog_test_run_opts(prog_fd, &topts);
1550-
ASSERT_OK(err, "test_run");
1551-
ASSERT_EQ(topts.retval, 34, "test_run retval");
1613+
err = bpf_map_delete_elem(map_fd, &key);
1614+
if (!ASSERT_OK(err, "delete_elem from jmp_table"))
1615+
goto out;
1616+
1617+
/* Fail to attach a tail callee prog with freplace prog. */
1618+
1619+
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
1620+
if (!ASSERT_OK(err, "update jmp_table"))
1621+
goto out;
1622+
1623+
freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
1624+
prog_fd, "subprog_tc");
1625+
if (!ASSERT_ERR_PTR(freplace_link, "attach_freplace failure"))
1626+
goto out;
1627+
1628+
err = bpf_map_delete_elem(map_fd, &key);
1629+
if (!ASSERT_OK(err, "delete_elem from jmp_table"))
1630+
goto out;
1631+
1632+
/* Fail to update an extended prog to prog_array map. */
1633+
1634+
freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
1635+
prog_fd, "subprog_tc");
1636+
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
1637+
goto out;
1638+
1639+
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
1640+
if (!ASSERT_ERR(err, "update jmp_table failure"))
1641+
goto out;
15521642

15531643
out:
15541644
bpf_link__destroy(freplace_link);
1555-
tc_bpf2bpf__destroy(tc_skel);
15561645
tailcall_freplace__destroy(freplace_skel);
1646+
tc_bpf2bpf__destroy(tc_skel);
15571647
}
15581648

15591649
void test_tailcalls(void)
@@ -1606,4 +1696,6 @@ void test_tailcalls(void)
16061696
test_tailcall_bpf2bpf_hierarchy_3();
16071697
if (test__start_subtest("tailcall_freplace"))
16081698
test_tailcall_freplace();
1699+
if (test__start_subtest("tailcall_bpf2bpf_freplace"))
1700+
test_tailcall_bpf2bpf_freplace();
16091701
}

0 commit comments

Comments
 (0)