selftests/bpf: Add test to verify tailcall and freplace restrictions

Add a test case to ensure that attaching a tail callee program with an
freplace program fails, and that updating an extended program to a
prog_array map is also prohibited.

This test is designed to prevent the potential infinite loop issue caused
by the combination of tail calls and freplace, ensuring the correct
behavior and stability of the system.

Additionally, fix the broken tailcalls/tailcall_freplace selftest
because an extension prog should not be tailcalled.

cd tools/testing/selftests/bpf; ./test_progs -t tailcalls
337/25  tailcalls/tailcall_freplace:OK
337/26  tailcalls/tailcall_bpf2bpf_freplace:OK
337     tailcalls:OK
Summary: 1/26 PASSED, 0 SKIPPED, 0 FAILED

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
Link: https://lore.kernel.org/r/20241015150207.70264-3-leon.hwang@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Leon Hwang 2024-10-15 23:02:07 +08:00 committed by Alexei Starovoitov
parent d6083f040d
commit 021611d33e
2 changed files with 111 additions and 18 deletions

View file

@ -1496,8 +1496,8 @@ static void test_tailcall_bpf2bpf_hierarchy_3(void)
RUN_TESTS(tailcall_bpf2bpf_hierarchy3);
}
/* test_tailcall_freplace checks that the attached freplace prog is OK to
* update the prog_array map.
/* test_tailcall_freplace checks that the freplace prog fails to update the
* prog_array map, no matter whether the freplace prog attaches to its target.
*/
static void test_tailcall_freplace(void)
{
@ -1505,7 +1505,7 @@ static void test_tailcall_freplace(void)
struct bpf_link *freplace_link = NULL;
struct bpf_program *freplace_prog;
struct tc_bpf2bpf *tc_skel = NULL;
int prog_fd, map_fd;
int prog_fd, tc_prog_fd, map_fd;
char buff[128] = {};
int err, key;
@ -1523,9 +1523,10 @@ static void test_tailcall_freplace(void)
if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load"))
goto out;
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
tc_prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
freplace_prog = freplace_skel->progs.entry_freplace;
err = bpf_program__set_attach_target(freplace_prog, prog_fd, "subprog");
err = bpf_program__set_attach_target(freplace_prog, tc_prog_fd,
"subprog_tc");
if (!ASSERT_OK(err, "set_attach_target"))
goto out;
@ -1533,27 +1534,116 @@ static void test_tailcall_freplace(void)
if (!ASSERT_OK(err, "tailcall_freplace__load"))
goto out;
freplace_link = bpf_program__attach_freplace(freplace_prog, prog_fd,
"subprog");
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
goto out;
map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
prog_fd = bpf_program__fd(freplace_prog);
key = 0;
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
if (!ASSERT_OK(err, "update jmp_table"))
ASSERT_ERR(err, "update jmp_table failure");
freplace_link = bpf_program__attach_freplace(freplace_prog, tc_prog_fd,
"subprog_tc");
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
goto out;
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
err = bpf_prog_test_run_opts(prog_fd, &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, 34, "test_run retval");
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
ASSERT_ERR(err, "update jmp_table failure");
out:
bpf_link__destroy(freplace_link);
tc_bpf2bpf__destroy(tc_skel);
tailcall_freplace__destroy(freplace_skel);
tc_bpf2bpf__destroy(tc_skel);
}
/* test_tailcall_bpf2bpf_freplace checks the failure that fails to attach a tail
* callee prog with freplace prog or fails to update an extended prog to
* prog_array map.
*/
static void test_tailcall_bpf2bpf_freplace(void)
{
struct tailcall_freplace *freplace_skel = NULL;
struct bpf_link *freplace_link = NULL;
struct tc_bpf2bpf *tc_skel = NULL;
char buff[128] = {};
int prog_fd, map_fd;
int err, key;
LIBBPF_OPTS(bpf_test_run_opts, topts,
.data_in = buff,
.data_size_in = sizeof(buff),
.repeat = 1,
);
tc_skel = tc_bpf2bpf__open_and_load();
if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load"))
goto out;
prog_fd = bpf_program__fd(tc_skel->progs.entry_tc);
freplace_skel = tailcall_freplace__open();
if (!ASSERT_OK_PTR(freplace_skel, "tailcall_freplace__open"))
goto out;
err = bpf_program__set_attach_target(freplace_skel->progs.entry_freplace,
prog_fd, "subprog_tc");
if (!ASSERT_OK(err, "set_attach_target"))
goto out;
err = tailcall_freplace__load(freplace_skel);
if (!ASSERT_OK(err, "tailcall_freplace__load"))
goto out;
/* OK to attach then detach freplace prog. */
freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
prog_fd, "subprog_tc");
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
goto out;
err = bpf_link__destroy(freplace_link);
if (!ASSERT_OK(err, "destroy link"))
goto out;
/* OK to update prog_array map then delete element from the map. */
key = 0;
map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
if (!ASSERT_OK(err, "update jmp_table"))
goto out;
err = bpf_map_delete_elem(map_fd, &key);
if (!ASSERT_OK(err, "delete_elem from jmp_table"))
goto out;
/* Fail to attach a tail callee prog with freplace prog. */
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
if (!ASSERT_OK(err, "update jmp_table"))
goto out;
freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
prog_fd, "subprog_tc");
if (!ASSERT_ERR_PTR(freplace_link, "attach_freplace failure"))
goto out;
err = bpf_map_delete_elem(map_fd, &key);
if (!ASSERT_OK(err, "delete_elem from jmp_table"))
goto out;
/* Fail to update an extended prog to prog_array map. */
freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace,
prog_fd, "subprog_tc");
if (!ASSERT_OK_PTR(freplace_link, "attach_freplace"))
goto out;
err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
if (!ASSERT_ERR(err, "update jmp_table failure"))
goto out;
out:
bpf_link__destroy(freplace_link);
tailcall_freplace__destroy(freplace_skel);
tc_bpf2bpf__destroy(tc_skel);
}
void test_tailcalls(void)
@ -1606,4 +1696,6 @@ void test_tailcalls(void)
test_tailcall_bpf2bpf_hierarchy_3();
if (test__start_subtest("tailcall_freplace"))
test_tailcall_freplace();
if (test__start_subtest("tailcall_bpf2bpf_freplace"))
test_tailcall_bpf2bpf_freplace();
}

View file

@ -5,10 +5,11 @@
#include "bpf_misc.h"
__noinline
int subprog(struct __sk_buff *skb)
int subprog_tc(struct __sk_buff *skb)
{
int ret = 1;
__sink(skb);
__sink(ret);
return ret;
}
@ -16,7 +17,7 @@ int subprog(struct __sk_buff *skb)
SEC("tc")
int entry_tc(struct __sk_buff *skb)
{
return subprog(skb);
return subprog_tc(skb);
}
char __license[] SEC("license") = "GPL";