selftests/bpf: Test sleepable global subprogs in atomic contexts
authorKumar Kartikeya Dwivedi <memxor@gmail.com>
Sat, 1 Mar 2025 15:18:45 +0000 (07:18 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 15 Mar 2025 18:48:25 +0000 (11:48 -0700)
Add tests for rejecting sleepable and accepting non-sleepable global
function calls in atomic contexts. For spin locks, we still reject
all global function calls. Once resilient spin locks land, we will
carefully lift in cases where we deem it safe.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250301151846.1552362-3-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c
tools/testing/selftests/bpf/prog_tests/spin_lock.c
tools/testing/selftests/bpf/progs/irq.c
tools/testing/selftests/bpf/progs/preempt_lock.c
tools/testing/selftests/bpf/progs/rcu_read_lock.c
tools/testing/selftests/bpf/progs/test_spin_lock_fail.c

index ebe0c12b55363c7976e03016caba1511b157a2cb..c9f855e5da2409fa539d0a5c9006f240848bccb8 100644 (file)
@@ -81,6 +81,9 @@ static const char * const inproper_region_tests[] = {
        "nested_rcu_region",
        "rcu_read_lock_global_subprog_lock",
        "rcu_read_lock_global_subprog_unlock",
+       "rcu_read_lock_sleepable_helper_global_subprog",
+       "rcu_read_lock_sleepable_kfunc_global_subprog",
+       "rcu_read_lock_sleepable_global_subprog_indirect",
 };
 
 static void test_inproper_region(void)
index 2b0068742ef9f00e1926dcabdda4d772ad56e4df..e3ea5dc2f697c40da1e0c4928b295369205fb9bd 100644 (file)
@@ -50,6 +50,9 @@ static struct {
        { "lock_id_mismatch_innermapval_mapval", "bpf_spin_unlock of different lock" },
        { "lock_global_subprog_call1", "global function calls are not allowed while holding a lock" },
        { "lock_global_subprog_call2", "global function calls are not allowed while holding a lock" },
+       { "lock_global_sleepable_helper_subprog", "global function calls are not allowed while holding a lock" },
+       { "lock_global_sleepable_kfunc_subprog", "global function calls are not allowed while holding a lock" },
+       { "lock_global_sleepable_subprog_indirect", "global function calls are not allowed while holding a lock" },
 };
 
 static int match_regex(const char *pattern, const char *string)
index b0b53d98096421b4b86ce30244a42c8a24a7d4c8..298d48d7886d92fd3fc0cb65004ce1e86265bc90 100644 (file)
@@ -222,7 +222,7 @@ int __noinline global_local_irq_balance(void)
 }
 
 SEC("?tc")
-__failure __msg("global function calls are not allowed with IRQs disabled")
+__success
 int irq_global_subprog(struct __sk_buff *ctx)
 {
        unsigned long flags;
@@ -441,4 +441,73 @@ int irq_ooo_refs_array(struct __sk_buff *ctx)
        return 0;
 }
 
+int __noinline
+global_subprog(int i)
+{
+       if (i)
+               bpf_printk("%p", &i);
+       return i;
+}
+
+int __noinline
+global_sleepable_helper_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user(&i, sizeof(i), NULL);
+       return i;
+}
+
+int __noinline
+global_sleepable_kfunc_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
+       global_subprog(i);
+       return i;
+}
+
+int __noinline
+global_subprog_calling_sleepable_global(int i)
+{
+       if (!i)
+               global_sleepable_kfunc_subprog(i);
+       return i;
+}
+
+SEC("?syscall")
+__success
+int irq_non_sleepable_global_subprog(void *ctx)
+{
+       unsigned long flags;
+
+       bpf_local_irq_save(&flags);
+       global_subprog(0);
+       bpf_local_irq_restore(&flags);
+       return 0;
+}
+
+SEC("?syscall")
+__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
+int irq_sleepable_helper_global_subprog(void *ctx)
+{
+       unsigned long flags;
+
+       bpf_local_irq_save(&flags);
+       global_sleepable_helper_subprog(0);
+       bpf_local_irq_restore(&flags);
+       return 0;
+}
+
+SEC("?syscall")
+__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
+int irq_sleepable_global_subprog_indirect(void *ctx)
+{
+       unsigned long flags;
+
+       bpf_local_irq_save(&flags);
+       global_subprog_calling_sleepable_global(0);
+       bpf_local_irq_restore(&flags);
+       return 0;
+}
+
 char _license[] SEC("license") = "GPL";
index 6c5797bf0ead57d466bc9c91eb6cb58687047f8c..7d04254e61f1ce0c2d81451bfa4df605f484d6c1 100644 (file)
@@ -134,7 +134,7 @@ int __noinline preempt_global_subprog(void)
 }
 
 SEC("?tc")
-__failure __msg("global function calls are not allowed with preemption disabled")
+__success
 int preempt_global_subprog_test(struct __sk_buff *ctx)
 {
        preempt_disable();
@@ -143,4 +143,70 @@ int preempt_global_subprog_test(struct __sk_buff *ctx)
        return 0;
 }
 
+int __noinline
+global_subprog(int i)
+{
+       if (i)
+               bpf_printk("%p", &i);
+       return i;
+}
+
+int __noinline
+global_sleepable_helper_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user(&i, sizeof(i), NULL);
+       return i;
+}
+
+int __noinline
+global_sleepable_kfunc_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
+       global_subprog(i);
+       return i;
+}
+
+int __noinline
+global_subprog_calling_sleepable_global(int i)
+{
+       if (!i)
+               global_sleepable_kfunc_subprog(i);
+       return i;
+}
+
+SEC("?syscall")
+__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
+int preempt_global_sleepable_helper_subprog(struct __sk_buff *ctx)
+{
+       preempt_disable();
+       if (ctx->mark)
+               global_sleepable_helper_subprog(ctx->mark);
+       preempt_enable();
+       return 0;
+}
+
+SEC("?syscall")
+__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
+int preempt_global_sleepable_kfunc_subprog(struct __sk_buff *ctx)
+{
+       preempt_disable();
+       if (ctx->mark)
+               global_sleepable_kfunc_subprog(ctx->mark);
+       preempt_enable();
+       return 0;
+}
+
+SEC("?syscall")
+__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
+int preempt_global_sleepable_subprog_indirect(struct __sk_buff *ctx)
+{
+       preempt_disable();
+       if (ctx->mark)
+               global_subprog_calling_sleepable_global(ctx->mark);
+       preempt_enable();
+       return 0;
+}
+
 char _license[] SEC("license") = "GPL";
index ab3a532b7dd6d28ade18760117716fbdd8656ce5..5cf1ae637ec7f05c4ace669447dd80fa5eba73b5 100644 (file)
@@ -439,3 +439,61 @@ int rcu_read_lock_global_subprog_unlock(void *ctx)
        ret += global_subprog_unlock(ret);
        return 0;
 }
+
+int __noinline
+global_sleepable_helper_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user(&i, sizeof(i), NULL);
+       return i;
+}
+
+int __noinline
+global_sleepable_kfunc_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
+       global_subprog(i);
+       return i;
+}
+
+int __noinline
+global_subprog_calling_sleepable_global(int i)
+{
+       if (!i)
+               global_sleepable_kfunc_subprog(i);
+       return i;
+}
+
+SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
+int rcu_read_lock_sleepable_helper_global_subprog(void *ctx)
+{
+       volatile int ret = 0;
+
+       bpf_rcu_read_lock();
+       ret += global_sleepable_helper_subprog(ret);
+       bpf_rcu_read_unlock();
+       return 0;
+}
+
+SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
+int rcu_read_lock_sleepable_kfunc_global_subprog(void *ctx)
+{
+       volatile int ret = 0;
+
+       bpf_rcu_read_lock();
+       ret += global_sleepable_kfunc_subprog(ret);
+       bpf_rcu_read_unlock();
+       return 0;
+}
+
+SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
+int rcu_read_lock_sleepable_global_subprog_indirect(void *ctx)
+{
+       volatile int ret = 0;
+
+       bpf_rcu_read_lock();
+       ret += global_subprog_calling_sleepable_global(ret);
+       bpf_rcu_read_unlock();
+       return 0;
+}
index 1c8b678e2e9a397786bb02d3de559b3ae50205cf..f678ee6bd7eafbb1d3ea9fd009da6da1c0023a6a 100644 (file)
@@ -245,4 +245,73 @@ int lock_global_subprog_call2(struct __sk_buff *ctx)
        return ret;
 }
 
+int __noinline
+global_subprog_int(int i)
+{
+       if (i)
+               bpf_printk("%p", &i);
+       return i;
+}
+
+int __noinline
+global_sleepable_helper_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user(&i, sizeof(i), NULL);
+       return i;
+}
+
+int __noinline
+global_sleepable_kfunc_subprog(int i)
+{
+       if (i)
+               bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
+       global_subprog_int(i);
+       return i;
+}
+
+int __noinline
+global_subprog_calling_sleepable_global(int i)
+{
+       if (!i)
+               global_sleepable_kfunc_subprog(i);
+       return i;
+}
+
+SEC("?syscall")
+int lock_global_sleepable_helper_subprog(struct __sk_buff *ctx)
+{
+       int ret = 0;
+
+       bpf_spin_lock(&lockA);
+       if (ctx->mark == 42)
+               ret = global_sleepable_helper_subprog(ctx->mark);
+       bpf_spin_unlock(&lockA);
+       return ret;
+}
+
+SEC("?syscall")
+int lock_global_sleepable_kfunc_subprog(struct __sk_buff *ctx)
+{
+       int ret = 0;
+
+       bpf_spin_lock(&lockA);
+       if (ctx->mark == 42)
+               ret = global_sleepable_kfunc_subprog(ctx->mark);
+       bpf_spin_unlock(&lockA);
+       return ret;
+}
+
+SEC("?syscall")
+int lock_global_sleepable_subprog_indirect(struct __sk_buff *ctx)
+{
+       int ret = 0;
+
+       bpf_spin_lock(&lockA);
+       if (ctx->mark == 42)
+               ret = global_subprog_calling_sleepable_global(ctx->mark);
+       bpf_spin_unlock(&lockA);
+       return ret;
+}
+
 char _license[] SEC("license") = "GPL";