Add tail-call selftests for prog-array ownership when cgroup storage is
in use. Verify that loading succeeds when callers and callees reuse the
owner's cgroup storage map, and that loading fails for a different
storage map or for a storage-less bridge program.

Signed-off-by: Lin Ma <[email protected]>
Signed-off-by: Rongzhen Cui <[email protected]>
Signed-off-by: Jingguo Tan <[email protected]>
---
 .../selftests/bpf/prog_tests/tailcalls.c      | 103 ++++++++++++++++++
 .../bpf/progs/tailcall_cgrp_storage.c         |  44 ++++++++
 .../progs/tailcall_cgrp_storage_no_storage.c  |  20 ++++
 .../bpf/progs/tailcall_cgrp_storage_owner.c   |  32 ++++++
 4 files changed, 199 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 7d534fde0af9..c1403ab20eae 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"
 #include "tailcall_sleepable.skel.h"
 
 /* test_tailcall_1 checks basic functionality by patching multiple locations
@@ -1654,6 +1657,100 @@ static void test_tailcall_failure()
        RUN_TESTS(tailcall_fail);
 }
 
+static void test_tailcall_cgrp_storage(void)
+{
+       struct tailcall_cgrp_storage_owner *owner_skel = NULL;
+       struct tailcall_cgrp_storage *skel = NULL;
+       int err, key = 0, prog_array_fd, prog_fd, storage_map_fd;
+
+       owner_skel = tailcall_cgrp_storage_owner__open_and_load();
+       if (!ASSERT_OK_PTR(owner_skel, "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"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
+       if (!ASSERT_OK(err, "reuse_prog_array"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.storage_map, storage_map_fd);
+       if (!ASSERT_OK(err, "reuse_storage_map"))
+               goto out;
+
+       err = bpf_object__load(skel->obj);
+       if (!ASSERT_OK(err, "tailcall_cgrp_storage__load"))
+               goto out;
+
+       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, "update_prog_array");
+
+out:
+       tailcall_cgrp_storage__destroy(skel);
+       tailcall_cgrp_storage_owner__destroy(owner_skel);
+}
+
+static void test_tailcall_cgrp_storage_diff_storage(void)
+{
+       struct tailcall_cgrp_storage_owner *owner_skel = NULL;
+       struct tailcall_cgrp_storage *skel = NULL;
+       int err, prog_array_fd;
+
+       owner_skel = tailcall_cgrp_storage_owner__open_and_load();
+       if (!ASSERT_OK_PTR(owner_skel, "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"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
+       if (!ASSERT_OK(err, "reuse_prog_array"))
+               goto out;
+
+       err = bpf_object__load(skel->obj);
+       ASSERT_ERR(err, "tailcall_cgrp_storage__load");
+
+out:
+       tailcall_cgrp_storage__destroy(skel);
+       tailcall_cgrp_storage_owner__destroy(owner_skel);
+}
+
+static void test_tailcall_cgrp_storage_no_storage(void)
+{
+       struct tailcall_cgrp_storage_owner *owner_skel = NULL;
+       struct tailcall_cgrp_storage_no_storage *skel = NULL;
+       int err, prog_array_fd;
+
+       owner_skel = tailcall_cgrp_storage_owner__open_and_load();
+       if (!ASSERT_OK_PTR(owner_skel, "owner_open_and_load"))
+               return;
+
+       prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);
+
+       skel = tailcall_cgrp_storage_no_storage__open();
+       if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage_no_storage__open"))
+               goto out;
+
+       err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
+       if (!ASSERT_OK(err, "reuse_prog_array"))
+               goto out;
+
+       err = bpf_object__load(skel->obj);
+       ASSERT_ERR(err, "tailcall_cgrp_storage_no_storage__load");
+
+out:
+       tailcall_cgrp_storage_no_storage__destroy(skel);
+       tailcall_cgrp_storage_owner__destroy(owner_skel);
+}
+
 noinline void uprobe_sleepable_trigger(void)
 {
        asm volatile ("");
@@ -1777,6 +1874,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();
        if (test__start_subtest("tailcall_sleepable"))
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..4dd3a0033d75
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+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..f95166dad7c5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_no_storage.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+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..d7e8ec9855c5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+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.53.0

Reply via email to