Merge branch 'bpf-consolidate-sleepable-context-checks-in-verifier'

Puranjay Mohan says:

====================
bpf: Consolidate sleepable context checks in verifier

The BPF verifier has multiple call-checking functions that independently
validate whether sleepable operations are permitted in the current
context. Each function open-codes its own checks against active_rcu_locks,
active_preempt_locks, active_irq_id, and in_sleepable, duplicating the
logic already provided by in_sleepable_context().

This series consolidates these scattered checks into calls to
in_sleepable_context() across check_helper_call(), check_kfunc_call(),
and check_func_call(), reducing code duplication and making the error
reporting consistent. No functional change.
====================

Link: https://patch.msgid.link/20260318174327.3151925-1-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-03-21 13:09:35 -07:00
commit 21337b58f5
5 changed files with 43 additions and 64 deletions

View File

@ -210,6 +210,8 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env);
static int ref_set_non_owning(struct bpf_verifier_env *env,
struct bpf_reg_state *reg);
static bool is_trusted_reg(const struct bpf_reg_state *reg);
static inline bool in_sleepable_context(struct bpf_verifier_env *env);
static const char *non_sleepable_context_description(struct bpf_verifier_env *env);
static bool bpf_map_ptr_poisoned(const struct bpf_insn_aux_data *aux)
{
@ -10948,12 +10950,9 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return -EINVAL;
}
if (env->subprog_info[subprog].might_sleep &&
(env->cur_state->active_rcu_locks || env->cur_state->active_preempt_locks ||
env->cur_state->active_irq_id || !in_sleepable(env))) {
verbose(env, "global functions that may sleep are not allowed in non-sleepable context,\n"
"i.e., in a RCU/IRQ/preempt-disabled section, or in\n"
"a non-sleepable BPF program context\n");
if (env->subprog_info[subprog].might_sleep && !in_sleepable_context(env)) {
verbose(env, "sleepable global function %s() called in %s\n",
sub_name, non_sleepable_context_description(env));
return -EINVAL;
}
@ -11678,6 +11677,19 @@ static inline bool in_sleepable_context(struct bpf_verifier_env *env)
in_sleepable(env);
}
static const char *non_sleepable_context_description(struct bpf_verifier_env *env)
{
if (env->cur_state->active_rcu_locks)
return "rcu_read_lock region";
if (env->cur_state->active_preempt_locks)
return "non-preemptible region";
if (env->cur_state->active_irq_id)
return "IRQ-disabled region";
if (env->cur_state->active_locks)
return "lock region";
return "non-sleepable prog";
}
static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
int *insn_idx_p)
{
@ -11717,11 +11729,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
return -EINVAL;
}
if (!in_sleepable(env) && fn->might_sleep) {
verbose(env, "helper call might sleep in a non-sleepable prog\n");
return -EINVAL;
}
/* With LD_ABS/IND some JITs save/restore skb from r1. */
changes_data = bpf_helper_changes_pkt_data(func_id);
if (changes_data && fn->arg1_type != ARG_PTR_TO_CTX) {
@ -11738,28 +11745,10 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
return err;
}
if (env->cur_state->active_rcu_locks) {
if (fn->might_sleep) {
verbose(env, "sleepable helper %s#%d in rcu_read_lock region\n",
func_id_name(func_id), func_id);
return -EINVAL;
}
}
if (env->cur_state->active_preempt_locks) {
if (fn->might_sleep) {
verbose(env, "sleepable helper %s#%d in non-preemptible region\n",
func_id_name(func_id), func_id);
return -EINVAL;
}
}
if (env->cur_state->active_irq_id) {
if (fn->might_sleep) {
verbose(env, "sleepable helper %s#%d in IRQ-disabled region\n",
func_id_name(func_id), func_id);
return -EINVAL;
}
if (fn->might_sleep && !in_sleepable_context(env)) {
verbose(env, "sleepable helper %s#%d in %s\n", func_id_name(func_id), func_id,
non_sleepable_context_description(env));
return -EINVAL;
}
/* Track non-sleepable context for helpers. */
@ -14286,8 +14275,19 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
}));
}
} else if (sleepable && env->cur_state->active_rcu_locks) {
verbose(env, "kernel func %s is sleepable within rcu_read_lock region\n", func_name);
} else if (preempt_disable) {
env->cur_state->active_preempt_locks++;
} else if (preempt_enable) {
if (env->cur_state->active_preempt_locks == 0) {
verbose(env, "unmatched attempt to enable preemption (kernel function %s)\n", func_name);
return -EINVAL;
}
env->cur_state->active_preempt_locks--;
}
if (sleepable && !in_sleepable_context(env)) {
verbose(env, "kernel func %s is sleepable within %s\n",
func_name, non_sleepable_context_description(env));
return -EACCES;
}
@ -14296,27 +14296,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return -EACCES;
}
if (env->cur_state->active_preempt_locks) {
if (preempt_disable) {
env->cur_state->active_preempt_locks++;
} else if (preempt_enable) {
env->cur_state->active_preempt_locks--;
} else if (sleepable) {
verbose(env, "kernel func %s is sleepable within non-preemptible region\n", func_name);
return -EACCES;
}
} else if (preempt_disable) {
env->cur_state->active_preempt_locks++;
} else if (preempt_enable) {
verbose(env, "unmatched attempt to enable preemption (kernel function %s)\n", func_name);
return -EINVAL;
}
if (env->cur_state->active_irq_id && sleepable) {
verbose(env, "kernel func %s is sleepable within IRQ-disabled region\n", func_name);
return -EACCES;
}
if (is_kfunc_rcu_protected(&meta) && !in_rcu_cs(env)) {
verbose(env, "kernel func %s requires RCU critical section protection\n", func_name);
return -EACCES;

View File

@ -58,7 +58,7 @@ static void test_aux(const char *main_prog_name,
* this particular combination can be enabled.
*/
if (!strcmp("might_sleep", replacement) && err) {
ASSERT_HAS_SUBSTR(log, "helper call might sleep in a non-sleepable prog", "error log");
ASSERT_HAS_SUBSTR(log, "sleepable helper bpf_copy_from_user#", "error log");
ASSERT_EQ(err, -EINVAL, "err");
test__skip();
goto out;

View File

@ -490,7 +490,7 @@ int irq_non_sleepable_global_subprog(void *ctx)
}
SEC("?syscall")
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
__failure __msg("sleepable global function")
int irq_sleepable_helper_global_subprog(void *ctx)
{
unsigned long flags;
@ -502,7 +502,7 @@ int irq_sleepable_helper_global_subprog(void *ctx)
}
SEC("?syscall")
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
__failure __msg("sleepable global function")
int irq_sleepable_global_subprog_indirect(void *ctx)
{
unsigned long flags;

View File

@ -177,7 +177,7 @@ global_subprog_calling_sleepable_global(int i)
}
SEC("?syscall")
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
__failure __msg("sleepable global function")
int preempt_global_sleepable_helper_subprog(struct __sk_buff *ctx)
{
preempt_disable();
@ -188,7 +188,7 @@ int preempt_global_sleepable_helper_subprog(struct __sk_buff *ctx)
}
SEC("?syscall")
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
__failure __msg("sleepable global function")
int preempt_global_sleepable_kfunc_subprog(struct __sk_buff *ctx)
{
preempt_disable();
@ -199,7 +199,7 @@ int preempt_global_sleepable_kfunc_subprog(struct __sk_buff *ctx)
}
SEC("?syscall")
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
__failure __msg("sleepable global function")
int preempt_global_sleepable_subprog_indirect(struct __sk_buff *ctx)
{
preempt_disable();

View File

@ -31,7 +31,7 @@ static int timer_cb(void *map, int *key, struct bpf_timer *timer)
}
SEC("fentry/bpf_fentry_test1")
__failure __msg("helper call might sleep in a non-sleepable prog")
__failure __msg("sleepable helper bpf_copy_from_user#{{[0-9]+}} in non-sleepable prog")
int timer_non_sleepable_prog(void *ctx)
{
struct timer_elem *val;
@ -47,7 +47,7 @@ int timer_non_sleepable_prog(void *ctx)
}
SEC("lsm.s/file_open")
__failure __msg("helper call might sleep in a non-sleepable prog")
__failure __msg("sleepable helper bpf_copy_from_user#{{[0-9]+}} in non-sleepable prog")
int timer_sleepable_prog(void *ctx)
{
struct timer_elem *val;