Commit b2bb7034 authored by Kumar Kartikeya Dwivedi's avatar Kumar Kartikeya Dwivedi Committed by Alexei Starovoitov
Browse files

selftests/bpf: Test sleepable global subprogs in atomic contexts



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: default avatarKumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250301151846.1552362-3-memxor@gmail.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent e2d8f560
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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)
+3 −0
Original line number Diff line number Diff line
@@ -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)
+70 −1
Original line number Diff line number Diff line
@@ -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";
+67 −1
Original line number Diff line number Diff line
@@ -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";
+58 −0
Original line number Diff line number Diff line
@@ -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;
}
Loading