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


Reply via email to