Commit f0c1523f authored by Leon Hwang's avatar Leon Hwang Committed by Tengda Wu
Browse files

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

mainline inclusion
from mainline-v6.13-rc1
commit 021611d33e78694f4bd54573093c6fc70a812644
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IBIADD
CVE: CVE-2024-47794

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=021611d33e78694f4bd54573093c6fc70a812644



--------------------------------

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: default avatarEduard Zingerman <eddyz87@gmail.com>
Signed-off-by: default avatarLeon Hwang <leon.hwang@linux.dev>
Link: https://lore.kernel.org/r/20241015150207.70264-3-leon.hwang@linux.dev


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Conflicts:
	tools/testing/selftests/bpf/prog_tests/tailcalls.c
[The conflict is due to we did not backport b83b936f3e9a testcases]
Signed-off-by: default avatarTengda Wu <wutengda2@huawei.com>
parent 46a69aae
Loading
Loading
Loading
Loading
+106 −14
Original line number Diff line number Diff line
@@ -886,8 +886,8 @@ static void test_tailcall_bpf2bpf_6(void)
	tailcall_bpf2bpf6__destroy(obj);
}

/* 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)
{
@@ -895,7 +895,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;

@@ -913,9 +913,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;

@@ -923,27 +924,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");
	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);
	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;

	map_fd = bpf_map__fd(freplace_skel->maps.jmp_table);
	prog_fd = bpf_program__fd(freplace_prog);
	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);
	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;

	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_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);
	tc_bpf2bpf__destroy(tc_skel);
	tailcall_freplace__destroy(freplace_skel);
	tc_bpf2bpf__destroy(tc_skel);
}

void test_tailcalls(void)
@@ -974,4 +1064,6 @@ void test_tailcalls(void)
		test_tailcall_bpf2bpf_6();
	if (test__start_subtest("tailcall_freplace"))
		test_tailcall_freplace();
	if (test__start_subtest("tailcall_bpf2bpf_freplace"))
		test_tailcall_bpf2bpf_freplace();
}
+3 −2
Original line number Diff line number Diff line
@@ -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";