| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates.*/ |
| |
| #define _GNU_SOURCE |
| #include <unistd.h> |
| #include <sys/syscall.h> |
| #include <sys/types.h> |
| #include <test_progs.h> |
| #include <bpf/btf.h> |
| #include "rcu_read_lock.skel.h" |
| #include "cgroup_helpers.h" |
| |
| static unsigned long long cgroup_id; |
| |
| static void test_success(void) |
| { |
| struct rcu_read_lock *skel; |
| int err; |
| |
| skel = rcu_read_lock__open(); |
| if (!ASSERT_OK_PTR(skel, "skel_open")) |
| return; |
| |
| skel->bss->target_pid = syscall(SYS_gettid); |
| |
| bpf_program__set_autoload(skel->progs.get_cgroup_id, true); |
| bpf_program__set_autoload(skel->progs.task_succ, true); |
| bpf_program__set_autoload(skel->progs.no_lock, true); |
| bpf_program__set_autoload(skel->progs.two_regions, true); |
| bpf_program__set_autoload(skel->progs.non_sleepable_1, true); |
| bpf_program__set_autoload(skel->progs.non_sleepable_2, true); |
| err = rcu_read_lock__load(skel); |
| if (!ASSERT_OK(err, "skel_load")) |
| goto out; |
| |
| err = rcu_read_lock__attach(skel); |
| if (!ASSERT_OK(err, "skel_attach")) |
| goto out; |
| |
| syscall(SYS_getpgid); |
| |
| ASSERT_EQ(skel->bss->task_storage_val, 2, "task_storage_val"); |
| ASSERT_EQ(skel->bss->cgroup_id, cgroup_id, "cgroup_id"); |
| out: |
| rcu_read_lock__destroy(skel); |
| } |
| |
| static void test_rcuptr_acquire(void) |
| { |
| struct rcu_read_lock *skel; |
| int err; |
| |
| skel = rcu_read_lock__open(); |
| if (!ASSERT_OK_PTR(skel, "skel_open")) |
| return; |
| |
| skel->bss->target_pid = syscall(SYS_gettid); |
| |
| bpf_program__set_autoload(skel->progs.task_acquire, true); |
| err = rcu_read_lock__load(skel); |
| if (!ASSERT_OK(err, "skel_load")) |
| goto out; |
| |
| err = rcu_read_lock__attach(skel); |
| ASSERT_OK(err, "skel_attach"); |
| out: |
| rcu_read_lock__destroy(skel); |
| } |
| |
| static const char * const inproper_region_tests[] = { |
| "miss_lock", |
| "miss_unlock", |
| "non_sleepable_rcu_mismatch", |
| "inproper_sleepable_helper", |
| "inproper_sleepable_kfunc", |
| "nested_rcu_region", |
| }; |
| |
| static void test_inproper_region(void) |
| { |
| struct rcu_read_lock *skel; |
| struct bpf_program *prog; |
| int i, err; |
| |
| for (i = 0; i < ARRAY_SIZE(inproper_region_tests); i++) { |
| skel = rcu_read_lock__open(); |
| if (!ASSERT_OK_PTR(skel, "skel_open")) |
| return; |
| |
| prog = bpf_object__find_program_by_name(skel->obj, inproper_region_tests[i]); |
| if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) |
| goto out; |
| bpf_program__set_autoload(prog, true); |
| err = rcu_read_lock__load(skel); |
| ASSERT_ERR(err, "skel_load"); |
| out: |
| rcu_read_lock__destroy(skel); |
| } |
| } |
| |
| static const char * const rcuptr_misuse_tests[] = { |
| "task_untrusted_non_rcuptr", |
| "task_untrusted_rcuptr", |
| "cross_rcu_region", |
| }; |
| |
| static void test_rcuptr_misuse(void) |
| { |
| struct rcu_read_lock *skel; |
| struct bpf_program *prog; |
| int i, err; |
| |
| for (i = 0; i < ARRAY_SIZE(rcuptr_misuse_tests); i++) { |
| skel = rcu_read_lock__open(); |
| if (!ASSERT_OK_PTR(skel, "skel_open")) |
| return; |
| |
| prog = bpf_object__find_program_by_name(skel->obj, rcuptr_misuse_tests[i]); |
| if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) |
| goto out; |
| bpf_program__set_autoload(prog, true); |
| err = rcu_read_lock__load(skel); |
| ASSERT_ERR(err, "skel_load"); |
| out: |
| rcu_read_lock__destroy(skel); |
| } |
| } |
| |
| void test_rcu_read_lock(void) |
| { |
| struct btf *vmlinux_btf; |
| int cgroup_fd; |
| |
| vmlinux_btf = btf__load_vmlinux_btf(); |
| if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF")) |
| return; |
| if (btf__find_by_name_kind(vmlinux_btf, "rcu", BTF_KIND_TYPE_TAG) < 0) { |
| test__skip(); |
| goto out; |
| } |
| |
| cgroup_fd = test__join_cgroup("/rcu_read_lock"); |
| if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /rcu_read_lock")) |
| goto out; |
| |
| cgroup_id = get_cgroup_id("/rcu_read_lock"); |
| if (test__start_subtest("success")) |
| test_success(); |
| if (test__start_subtest("rcuptr_acquire")) |
| test_rcuptr_acquire(); |
| if (test__start_subtest("negative_tests_inproper_region")) |
| test_inproper_region(); |
| if (test__start_subtest("negative_tests_rcuptr_misuse")) |
| test_rcuptr_misuse(); |
| close(cgroup_fd); |
| out: |
| btf__free(vmlinux_btf); |
| } |