selftests: bpf: add bpf_cpumask_populate selftests
authorEmil Tsalapatis <emil@etsalapatis.com>
Sun, 9 Mar 2025 23:04:25 +0000 (19:04 -0400)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 15 Mar 2025 18:48:57 +0000 (11:48 -0700)
Add selftests for the bpf_cpumask_populate helper that sets a
bpf_cpumask to a bit pattern provided by a BPF program.

Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
Acked-by: Hou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/20250309230427.26603-3-emil@etsalapatis.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/cpumask.c
tools/testing/selftests/bpf/progs/cpumask_common.h
tools/testing/selftests/bpf/progs/cpumask_failure.c
tools/testing/selftests/bpf/progs/cpumask_success.c

index e58a04654238c9b4d1d366f9f0537675910cc153..9b09beba988b464bca8ad1f0a214085831f3565f 100644 (file)
@@ -25,6 +25,9 @@ static const char * const cpumask_success_testcases[] = {
        "test_global_mask_nested_deep_rcu",
        "test_global_mask_nested_deep_array_rcu",
        "test_cpumask_weight",
+       "test_populate_reject_small_mask",
+       "test_populate_reject_unaligned",
+       "test_populate",
 };
 
 static void verify_success(const char *prog_name)
index 4ece7873ba609d3e86e7aeb2f6337ab561e59f68..86085b79f5cac4a54aa6ae4c97fb3477af39aed6 100644 (file)
@@ -61,6 +61,7 @@ u32 bpf_cpumask_any_distribute(const struct cpumask *src) __ksym __weak;
 u32 bpf_cpumask_any_and_distribute(const struct cpumask *src1,
                                   const struct cpumask *src2) __ksym __weak;
 u32 bpf_cpumask_weight(const struct cpumask *cpumask) __ksym __weak;
+int bpf_cpumask_populate(struct cpumask *cpumask, void *src, size_t src__sz) __ksym __weak;
 
 void bpf_rcu_read_lock(void) __ksym __weak;
 void bpf_rcu_read_unlock(void) __ksym __weak;
index b40b52548ffb0ef6a6391e78cb20feff23bc70d3..8a2fd596c8a39848f22970c2f1f0015b2e01e628 100644 (file)
@@ -222,3 +222,41 @@ int BPF_PROG(test_invalid_nested_array, struct task_struct *task, u64 clone_flag
 
        return 0;
 }
+
+SEC("tp_btf/task_newtask")
+__failure __msg("type=scalar expected=fp")
+int BPF_PROG(test_populate_invalid_destination, struct task_struct *task, u64 clone_flags)
+{
+       struct bpf_cpumask *invalid = (struct bpf_cpumask *)0x123456;
+       u64 bits;
+       int ret;
+
+       ret = bpf_cpumask_populate((struct cpumask *)invalid, &bits, sizeof(bits));
+       if (!ret)
+               err = 2;
+
+       return 0;
+}
+
+SEC("tp_btf/task_newtask")
+__failure __msg("leads to invalid memory access")
+int BPF_PROG(test_populate_invalid_source, struct task_struct *task, u64 clone_flags)
+{
+       void *garbage = (void *)0x123456;
+       struct bpf_cpumask *local;
+       int ret;
+
+       local = create_cpumask();
+       if (!local) {
+               err = 1;
+               return 0;
+       }
+
+       ret = bpf_cpumask_populate((struct cpumask *)local, garbage, 8);
+       if (!ret)
+               err = 2;
+
+       bpf_cpumask_release(local);
+
+       return 0;
+}
index 80ee469b0b6028634308f05df383471bc6bcec70..91a5357769a8b5ec82194a34c83bfbcfe737ae52 100644 (file)
@@ -770,3 +770,122 @@ free_masks_return:
                bpf_cpumask_release(mask2);
        return 0;
 }
+
+SEC("tp_btf/task_newtask")
+int BPF_PROG(test_populate_reject_small_mask, struct task_struct *task, u64 clone_flags)
+{
+       struct bpf_cpumask *local;
+       u8 toofewbits;
+       int ret;
+
+       if (!is_test_task())
+               return 0;
+
+       local = create_cpumask();
+       if (!local)
+               return 0;
+
+       /* The kfunc should prevent this operation */
+       ret = bpf_cpumask_populate((struct cpumask *)local, &toofewbits, sizeof(toofewbits));
+       if (ret != -EACCES)
+               err = 2;
+
+       bpf_cpumask_release(local);
+
+       return 0;
+}
+
+/* Mask is guaranteed to be large enough for bpf_cpumask_t. */
+#define CPUMASK_TEST_MASKLEN (sizeof(cpumask_t))
+
+/* Add an extra word for the test_populate_reject_unaligned test. */
+u64 bits[CPUMASK_TEST_MASKLEN / 8 + 1];
+extern bool CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS __kconfig __weak;
+
+SEC("tp_btf/task_newtask")
+int BPF_PROG(test_populate_reject_unaligned, struct task_struct *task, u64 clone_flags)
+{
+       struct bpf_cpumask *mask;
+       char *src;
+       int ret;
+
+       if (!is_test_task())
+               return 0;
+
+       /* Skip if unaligned accesses are fine for this arch.  */
+       if (CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+               return 0;
+
+       mask = bpf_cpumask_create();
+       if (!mask) {
+               err = 1;
+               return 0;
+       }
+
+       /* Misalign the source array by a byte. */
+       src = &((char *)bits)[1];
+
+       ret = bpf_cpumask_populate((struct cpumask *)mask, src, CPUMASK_TEST_MASKLEN);
+       if (ret != -EINVAL)
+               err = 2;
+
+       bpf_cpumask_release(mask);
+
+       return 0;
+}
+
+
+SEC("tp_btf/task_newtask")
+int BPF_PROG(test_populate, struct task_struct *task, u64 clone_flags)
+{
+       struct bpf_cpumask *mask;
+       bool bit;
+       int ret;
+       int i;
+
+       if (!is_test_task())
+               return 0;
+
+       /* Set only odd bits. */
+       __builtin_memset(bits, 0xaa, CPUMASK_TEST_MASKLEN);
+
+       mask = bpf_cpumask_create();
+       if (!mask) {
+               err = 1;
+               return 0;
+       }
+
+       /* Pass the entire bits array, the kfunc will only copy the valid bits. */
+       ret = bpf_cpumask_populate((struct cpumask *)mask, bits, CPUMASK_TEST_MASKLEN);
+       if (ret) {
+               err = 2;
+               goto out;
+       }
+
+       /*
+        * Test is there to appease the verifier. We cannot directly
+        * access NR_CPUS, the upper bound for nr_cpus, so we infer
+        * it from the size of cpumask_t.
+        */
+       if (nr_cpus < 0 || nr_cpus >= CPUMASK_TEST_MASKLEN * 8) {
+               err = 3;
+               goto out;
+       }
+
+       bpf_for(i, 0, nr_cpus) {
+               /* Odd-numbered bits should be set, even ones unset. */
+               bit = bpf_cpumask_test_cpu(i, (const struct cpumask *)mask);
+               if (bit == (i % 2 != 0))
+                       continue;
+
+               err = 4;
+               break;
+       }
+
+out:
+       bpf_cpumask_release(mask);
+
+       return 0;
+}
+
+#undef CPUMASK_TEST_MASKLEN