Add focused dynptr selftest programs that load socket-filter programs using bpf_dynptr_from_mem() followed by each nofault probe-read dynptr kfunc: bpf_probe_read_user_dynptr(), bpf_probe_read_kernel_dynptr(), bpf_probe_read_user_str_dynptr(), and bpf_probe_read_kernel_str_dynptr(). Declare the dynptr kfuncs used by this BPF object locally in dynptr_success.c so the dynptr BPF object builds with explicit prototypes.
The harness first verifies that the program loads with the caller's normal capabilities. It then drops CAP_PERFMON and CAP_SYS_ADMIN while keeping CAP_BPF effective and expects each same program load to fail with -EACCES. This covers the policy boundary that these dynptr probe-read kfuncs should not be callable with CAP_BPF alone. Signed-off-by: Nuoqi Gui <[email protected]> --- tools/testing/selftests/bpf/prog_tests/dynptr.c | 66 +++++++++++++++++++ tools/testing/selftests/bpf/progs/dynptr_success.c | 75 ++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index 5fda11590708..5235b47e3342 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -3,6 +3,7 @@ #include <test_progs.h> #include <network_helpers.h> +#include "cap_helpers.h" #include "dynptr_fail.skel.h" #include "dynptr_success.skel.h" @@ -53,8 +54,66 @@ static struct { {"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP}, }; +static const char * const probe_read_cap_tests[] = { + "test_probe_read_user_dynptr_cap", + "test_probe_read_kernel_dynptr_cap", + "test_probe_read_user_str_dynptr_cap", + "test_probe_read_kernel_str_dynptr_cap", +}; + #define PAGE_SIZE_64K 65536 +static int load_probe_read_cap_prog(const char *prog_name) +{ + struct bpf_program *prog, *tmp; + struct dynptr_success *skel; + int err; + + skel = dynptr_success__open(); + if (!ASSERT_OK_PTR(skel, "dynptr_success__open")) + return -EINVAL; + + bpf_object__for_each_program(tmp, skel->obj) + bpf_program__set_autoload(tmp, false); + + prog = bpf_object__find_program_by_name(skel->obj, prog_name); + if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) { + err = -ENOENT; + goto cleanup; + } + + bpf_program__set_autoload(prog, true); + err = dynptr_success__load(skel); + +cleanup: + dynptr_success__destroy(skel); + return err; +} + +static void verify_probe_read_cap(const char *prog_name) +{ + __u64 old_caps = 0; + int err; + + err = load_probe_read_cap_prog(prog_name); + if (!ASSERT_OK(err, "full_caps_load")) + return; + + err = cap_enable_effective(1ULL << CAP_BPF, &old_caps); + if (!ASSERT_OK(err, "enable_cap_bpf")) + return; + + err = cap_disable_effective(1ULL << CAP_SYS_ADMIN | 1ULL << CAP_PERFMON, NULL); + if (!ASSERT_OK(err, "disable_cap_sys_admin_perfmon")) + goto restore_cap; + + err = load_probe_read_cap_prog(prog_name); + ASSERT_EQ(err, -EACCES, "cap_bpf_without_perfmon_load"); + +restore_cap: + cap_enable_effective(old_caps, NULL); +} + static void verify_success(const char *prog_name, enum test_setup_type setup_type) { char user_data[384] = {[0 ... 382] = 'a', '\0'}; @@ -185,6 +244,13 @@ void test_dynptr(void) { int i; + for (i = 0; i < ARRAY_SIZE(probe_read_cap_tests); i++) { + if (!test__start_subtest(probe_read_cap_tests[i])) + continue; + + verify_probe_read_cap(probe_read_cap_tests[i]); + } + for (i = 0; i < ARRAY_SIZE(success_tests); i++) { if (!test__start_subtest(success_tests[i].prog_name)) continue; diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c index e0745b6e467e..5b4e22198598 100644 --- a/tools/testing/selftests/bpf/progs/dynptr_success.c +++ b/tools/testing/selftests/bpf/progs/dynptr_success.c @@ -34,6 +34,13 @@ struct { __type(value, __u32); } array_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u8[32]); +} probe_read_map SEC(".maps"); + SEC("?tp/syscalls/sys_enter_nanosleep") int test_read_write(void *ctx) { @@ -1092,6 +1099,74 @@ int test_probe_read_kernel_str_dynptr(struct xdp_md *xdp) return XDP_PASS; } +SEC("?socket") +int test_probe_read_user_dynptr_cap(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + __u32 key = 0; + void *value; + + value = bpf_map_lookup_elem(&probe_read_map, &key); + if (!value) + return 0; + + if (bpf_dynptr_from_mem(value, 32, 0, &ptr)) + return 0; + + return bpf_probe_read_user_dynptr(&ptr, 0, 8, value); +} + +SEC("?socket") +int test_probe_read_kernel_dynptr_cap(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + __u32 key = 0; + void *value; + + value = bpf_map_lookup_elem(&probe_read_map, &key); + if (!value) + return 0; + + if (bpf_dynptr_from_mem(value, 32, 0, &ptr)) + return 0; + + return bpf_probe_read_kernel_dynptr(&ptr, 0, 8, value); +} + +SEC("?socket") +int test_probe_read_user_str_dynptr_cap(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + __u32 key = 0; + void *value; + + value = bpf_map_lookup_elem(&probe_read_map, &key); + if (!value) + return 0; + + if (bpf_dynptr_from_mem(value, 32, 0, &ptr)) + return 0; + + return bpf_probe_read_user_str_dynptr(&ptr, 0, 8, value); +} + +SEC("?socket") +int test_probe_read_kernel_str_dynptr_cap(struct __sk_buff *skb) +{ + struct bpf_dynptr ptr; + __u32 key = 0; + void *value; + + value = bpf_map_lookup_elem(&probe_read_map, &key); + if (!value) + return 0; + + if (bpf_dynptr_from_mem(value, 32, 0, &ptr)) + return 0; + + return bpf_probe_read_kernel_str_dynptr(&ptr, 0, 8, value); +} + SEC("fentry.s/" SYS_PREFIX "sys_nanosleep") int test_copy_from_user_dynptr(void *ctx) { -- 2.34.1

