Make sure that if the owner of a program array map uses cgroup storage, (1) all callers must use cgroup storage and (2) the cgroup storage map used by all callers and callees must be the owner's cgroup storage map. Signed-off-by: Amery Hung --- .../selftests/bpf/prog_tests/tailcalls.c | 119 ++++++++++++++++++ .../bpf/progs/tailcall_cgrp_storage.c | 45 +++++++ .../progs/tailcall_cgrp_storage_no_storage.c | 21 ++++ .../bpf/progs/tailcall_cgrp_storage_owner.c | 33 +++++ 4 files changed, 218 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c create mode 100644 tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_no_storage.c create mode 100644 tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c index 0ab36503c3b2..8ae4d101ed66 100644 --- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c +++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c @@ -8,6 +8,9 @@ #include "tailcall_freplace.skel.h" #include "tc_bpf2bpf.skel.h" #include "tailcall_fail.skel.h" +#include "tailcall_cgrp_storage_owner.skel.h" +#include "tailcall_cgrp_storage_no_storage.skel.h" +#include "tailcall_cgrp_storage.skel.h" /* test_tailcall_1 checks basic functionality by patching multiple locations * in a single program for a single tail call slot with nop->jmp, jmp->nop @@ -1648,6 +1651,116 @@ static void test_tailcall_bpf2bpf_freplace(void) tc_bpf2bpf__destroy(tc_skel); } +/* + * test_tail_call_cgrp_storage checks that if the owner program of a program + * array uses cgroup storage, other callers and callees must also use the + * exact same cgroup storage. + */ +static void test_tailcall_cgrp_storage(void) +{ + int err, prog_fd, prog_array_fd, storage_map_fd, key = 0; + struct tailcall_cgrp_storage_owner *owner_skel; + struct tailcall_cgrp_storage *skel; + + /* Load owner_skel first to make sure it becomes the owner of prog_array */ + owner_skel = tailcall_cgrp_storage_owner__open_and_load(); + if (!ASSERT_OK_PTR(owner_skel, "tailcall_cgrp_storage_owner__open_and_load")) + return; + + prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array); + storage_map_fd = bpf_map__fd(owner_skel->maps.storage_map); + + skel = tailcall_cgrp_storage__open(); + if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage__open")) { + tailcall_cgrp_storage_owner__destroy(owner_skel); + return; + } + + err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd); + ASSERT_OK(err, "bpf_map__reuse_fd(prog_array)"); + + err = bpf_map__reuse_fd(skel->maps.storage_map, storage_map_fd); + ASSERT_OK(err, "bpf_map__reuse_fd(storage_map)"); + + err = bpf_object__load(skel->obj); + ASSERT_OK(err, "bpf_object__load"); + + prog_fd = bpf_program__fd(skel->progs.callee_prog); + + err = bpf_map_update_elem(prog_array_fd, &key, &prog_fd, BPF_ANY); + ASSERT_OK(err, "bpf_map_update_elem(prog_array)"); + + tailcall_cgrp_storage__destroy(skel); + tailcall_cgrp_storage_owner__destroy(owner_skel); +} + +/* + * test_tail_call_cgrp_storage_diff_storage checks that a program using tail call + * is rejected if it uses a cgroup storage different from the owner's. + */ +static void test_tailcall_cgrp_storage_diff_storage(void) +{ + struct tailcall_cgrp_storage_owner *owner_skel; + struct tailcall_cgrp_storage *skel; + int err, prog_array_fd; + + /* Load owner_skel first to make sure it becomes the owner of prog_array */ + owner_skel = tailcall_cgrp_storage_owner__open_and_load(); + if (!ASSERT_OK_PTR(owner_skel, "tailcall_cgrp_storage_owner__open_and_load")) + return; + + prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array); + + skel = tailcall_cgrp_storage__open(); + if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage__open")) { + tailcall_cgrp_storage_owner__destroy(owner_skel); + return; + } + + err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd); + ASSERT_OK(err, "bpf_map__reuse_fd(prog_array)"); + + err = bpf_object__load(skel->obj); + ASSERT_ERR(err, "bpf_object__load"); + + tailcall_cgrp_storage__destroy(skel); + tailcall_cgrp_storage_owner__destroy(owner_skel); +} + +/* + * test_tail_call_cgrp_storage_no_storage checks that a program using tail call + * is rejected if it does not use cgroup storage while the owner does. + */ +static void test_tailcall_cgrp_storage_no_storage(void) +{ + struct tailcall_cgrp_storage_owner *owner_skel; + struct tailcall_cgrp_storage_no_storage *skel; + int err, prog_array_fd, storage_map_fd; + + /* Load owner_skel first to make sure it becomes the owner of prog_array */ + owner_skel = tailcall_cgrp_storage_owner__open_and_load(); + if (!ASSERT_OK_PTR(owner_skel, "tailcall_cgrp_storage_owner__open_and_load")) + return; + + prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array); + storage_map_fd = bpf_map__fd(owner_skel->maps.storage_map); + + skel = tailcall_cgrp_storage_no_storage__open(); + if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage_no_storage__open")) { + tailcall_cgrp_storage_owner__destroy(owner_skel); + return; + } + + err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd); + ASSERT_OK(err, "bpf_map__reuse_fd(prog_array)"); + + err = bpf_object__load(skel->obj); + ASSERT_ERR(err, "bpf_object__load"); + + tailcall_cgrp_storage_no_storage__destroy(skel); + tailcall_cgrp_storage_owner__destroy(owner_skel); +} + static void test_tailcall_failure() { RUN_TESTS(tailcall_fail); @@ -1705,6 +1818,12 @@ void test_tailcalls(void) test_tailcall_freplace(); if (test__start_subtest("tailcall_bpf2bpf_freplace")) test_tailcall_bpf2bpf_freplace(); + if (test__start_subtest("tailcall_cgrp_storage")) + test_tailcall_cgrp_storage(); + if (test__start_subtest("tailcall_cgrp_storage_diff_storage")) + test_tailcall_cgrp_storage_diff_storage(); + if (test__start_subtest("tailcall_cgrp_storage_no_storage")) + test_tailcall_cgrp_storage_no_storage(); if (test__start_subtest("tailcall_failure")) test_tailcall_failure(); } diff --git a/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c new file mode 100644 index 000000000000..e8356f95fb0a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE); + __type(key, struct bpf_cgroup_storage_key); + __type(value, __u64); +} storage_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} prog_array SEC(".maps"); + +SEC("cgroup_skb/egress") +int caller_prog(struct __sk_buff *skb) +{ + __u64 *storage; + + storage = bpf_get_local_storage(&storage_map, 0); + if (storage) + *storage = 1; + + bpf_tail_call(skb, &prog_array, 0); + + return 1; +} + +SEC("cgroup_skb/egress") +int callee_prog(struct __sk_buff *skb) +{ + __u64 *storage; + + storage = bpf_get_local_storage(&storage_map, 0); + if (storage) + *storage = 1; + + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_no_storage.c b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_no_storage.c new file mode 100644 index 000000000000..2f295e66d488 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_no_storage.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} prog_array SEC(".maps"); + +SEC("cgroup_skb/egress") +int caller_prog(struct __sk_buff *skb) +{ + bpf_tail_call(skb, &prog_array, 0); + + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c new file mode 100644 index 000000000000..6ac195b800cf --- /dev/null +++ b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE); + __type(key, struct bpf_cgroup_storage_key); + __type(value, __u64); +} storage_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); + __uint(max_entries, 1); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u32)); +} prog_array SEC(".maps"); + +SEC("cgroup_skb/egress") +int prog_array_owner(struct __sk_buff *skb) +{ + __u64 *storage; + + storage = bpf_get_local_storage(&storage_map, 0); + if (storage) + *storage = 1; + + bpf_tail_call(skb, &prog_array, 0); + + return 1; +} + +char _license[] SEC("license") = "GPL"; -- 2.47.3