]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/bpf: Introduce __caps_unpriv annotation for tests
authorEduard Zingerman <eddyz87@gmail.com>
Wed, 4 Dec 2024 04:47:55 +0000 (20:47 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 4 Dec 2024 17:19:50 +0000 (09:19 -0800)
Add a __caps_unpriv annotation so that tests requiring specific
capabilities while dropping the rest can conveniently specify them
during selftest declaration instead of munging with capabilities at
runtime from the testing binary.

While at it, let us convert test_verifier_mtu to use this new support
instead.

Since we do not want to include linux/capability.h, we only defined the
four main capabilities BPF subsystem deals with in bpf_misc.h for use in
tests. If the user passes a CAP_SYS_NICE or anything else that's not
defined in the header, capability parsing code will return a warning.

Also reject strtol returning 0. CAP_CHOWN = 0 but we'll never need to
use it, and strtol doesn't errno on failed conversion. Fail the test in
such a case.

The original diff for this idea is available at link [0].

  [0]: https://lore.kernel.org/bpf/a1e48f5d9ae133e19adc6adf27e19d585e06bab4.camel@gmail.com

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
[ Kartikeya: rebase on bpf-next, add warn to parse_caps, convert test_verifier_mtu ]
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20241204044757.1483141-4-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/verifier.c
tools/testing/selftests/bpf/progs/bpf_misc.h
tools/testing/selftests/bpf/progs/verifier_mtu.c
tools/testing/selftests/bpf/test_loader.c

index d9f65adb456b6fb2afc469e413b3aa4c8bcb2088..3ee40ee9413a9e0fcd09bd1b4ec4df0c06ffbd09 100644 (file)
@@ -225,24 +225,7 @@ void test_verifier_xdp(void)                  { RUN(verifier_xdp); }
 void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
 void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
 void test_verifier_lsm(void)                  { RUN(verifier_lsm); }
-
-void test_verifier_mtu(void)
-{
-       __u64 caps = 0;
-       int ret;
-
-       /* In case CAP_BPF and CAP_PERFMON is not set */
-       ret = cap_enable_effective(1ULL << CAP_BPF | 1ULL << CAP_NET_ADMIN, &caps);
-       if (!ASSERT_OK(ret, "set_cap_bpf_cap_net_admin"))
-               return;
-       ret = cap_disable_effective(1ULL << CAP_SYS_ADMIN | 1ULL << CAP_PERFMON, NULL);
-       if (!ASSERT_OK(ret, "disable_cap_sys_admin"))
-               goto restore_cap;
-       RUN(verifier_mtu);
-restore_cap:
-       if (caps)
-               cap_enable_effective(caps, NULL);
-}
+void test_verifier_mtu(void)                 { RUN(verifier_mtu); }
 
 static int init_test_val_map(struct bpf_object *obj, char *map_name)
 {
index eccaf955e39470e16ae71c3d91216aba9bf9b1af..f45f4352feebdaa1900448e20a1b746d76c09921 100644 (file)
@@ -5,6 +5,10 @@
 #define XSTR(s) STR(s)
 #define STR(s) #s
 
+/* Expand a macro and then stringize the expansion */
+#define QUOTE(str) #str
+#define EXPAND_QUOTE(str) QUOTE(str)
+
 /* This set of attributes controls behavior of the
  * test_loader.c:test_loader__run_subtests().
  *
  * __arch_*          Specify on which architecture the test case should be tested.
  *                   Several __arch_* annotations could be specified at once.
  *                   When test case is not run on current arch it is marked as skipped.
+ * __caps_unpriv     Specify the capabilities that should be set when running the test.
  */
 #define __msg(msg)             __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
 #define __xlated(msg)          __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
 #define __arch_x86_64          __arch("X86_64")
 #define __arch_arm64           __arch("ARM64")
 #define __arch_riscv64         __arch("RISCV64")
+#define __caps_unpriv(caps)    __attribute__((btf_decl_tag("comment:test_caps_unpriv=" EXPAND_QUOTE(caps))))
+
+/* Define common capabilities tested using __caps_unpriv */
+#define CAP_NET_ADMIN          12
+#define CAP_SYS_ADMIN          21
+#define CAP_PERFMON            38
+#define CAP_BPF                        39
 
 /* Convenience macro for use with 'asm volatile' blocks */
 #define __naked __attribute__((naked))
index 70c7600a26a0a3399169f910e107af0f2290f99c..4ccf1ebc42d168cd45dcb136135ffd9e01565de1 100644 (file)
@@ -6,7 +6,9 @@
 
 SEC("tc/ingress")
 __description("uninit/mtu: write rejected")
-__failure __msg("invalid indirect read from stack")
+__success
+__caps_unpriv(CAP_BPF|CAP_NET_ADMIN)
+__failure_unpriv __msg_unpriv("invalid indirect read from stack")
 int tc_uninit_mtu(struct __sk_buff *ctx)
 {
        __u32 mtu;
index 3e9b009580d4e435c689509808b604d54cf7f184..53b06647cf57dbafdc8bb63c5f00b420ef84e358 100644 (file)
@@ -36,6 +36,7 @@
 #define TEST_TAG_ARCH "comment:test_arch="
 #define TEST_TAG_JITED_PFX "comment:test_jited="
 #define TEST_TAG_JITED_PFX_UNPRIV "comment:test_jited_unpriv="
+#define TEST_TAG_CAPS_UNPRIV "comment:test_caps_unpriv="
 
 /* Warning: duplicated in bpf_misc.h */
 #define POINTER_VALUE  0xcafe4all
@@ -74,6 +75,7 @@ struct test_subspec {
        struct expected_msgs jited;
        int retval;
        bool execute;
+       __u64 caps;
 };
 
 struct test_spec {
@@ -276,6 +278,37 @@ static int parse_int(const char *str, int *val, const char *name)
        return 0;
 }
 
+static int parse_caps(const char *str, __u64 *val, const char *name)
+{
+       int cap_flag = 0;
+       char *token = NULL, *saveptr = NULL;
+
+       char *str_cpy = strdup(str);
+       if (str_cpy == NULL) {
+               PRINT_FAIL("Memory allocation failed\n");
+               return -EINVAL;
+       }
+
+       token = strtok_r(str_cpy, "|", &saveptr);
+       while (token != NULL) {
+               errno = 0;
+               if (!strncmp("CAP_", token, sizeof("CAP_") - 1)) {
+                       PRINT_FAIL("define %s constant in bpf_misc.h, failed to parse caps\n", token);
+                       return -EINVAL;
+               }
+               cap_flag = strtol(token, NULL, 10);
+               if (!cap_flag || errno) {
+                       PRINT_FAIL("failed to parse caps %s\n", name);
+                       return -EINVAL;
+               }
+               *val |= (1ULL << cap_flag);
+               token = strtok_r(NULL, "|", &saveptr);
+       }
+
+       free(str_cpy);
+       return 0;
+}
+
 static int parse_retval(const char *str, int *val, const char *name)
 {
        struct {
@@ -541,6 +574,12 @@ static int parse_test_spec(struct test_loader *tester,
                        jit_on_next_line = true;
                } else if (str_has_pfx(s, TEST_BTF_PATH)) {
                        spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
+               } else if (str_has_pfx(s, TEST_TAG_CAPS_UNPRIV)) {
+                       val = s + sizeof(TEST_TAG_CAPS_UNPRIV) - 1;
+                       err = parse_caps(val, &spec->unpriv.caps, "test caps");
+                       if (err)
+                               goto cleanup;
+                       spec->mode_mask |= UNPRIV;
                }
        }
 
@@ -917,6 +956,13 @@ void run_subtest(struct test_loader *tester,
                        test__end_subtest();
                        return;
                }
+               if (subspec->caps) {
+                       err = cap_enable_effective(subspec->caps, NULL);
+                       if (err) {
+                               PRINT_FAIL("failed to set capabilities: %i, %s\n", err, strerror(err));
+                               goto subtest_cleanup;
+                       }
+               }
        }
 
        /* Implicitly reset to NULL if next test case doesn't specify */