Skip to content

Commit 90d8c89

Browse files
kkdwivediAlexei Starovoitov
authored andcommitted
bpf: Summarize sleepable global subprogs
The verifier currently does not permit global subprog calls when a lock is held, preemption is disabled, or when IRQs are disabled. This is because we don't know whether the global subprog calls sleepable functions or not. In case of locks, there's an additional reason: functions called by the global subprog may hold additional locks etc. The verifier won't know while verifying the global subprog whether it was called in context where a spin lock is already held by the program. Perform summarization of the sleepable nature of a global subprog just like changes_pkt_data and then allow calls to global subprogs for non-sleepable ones from atomic context. While making this change, I noticed that RCU read sections had no protection against sleepable global subprog calls, include it in the checks and fix this while we're at it. Care needs to be taken to not allow global subprog calls when regular bpf_spin_lock is held. When resilient spin locks is held, we want to potentially have this check relaxed, but not for now. Also make sure extensions freplacing global functions cannot do so in case the target is non-sleepable, but the extension is. The other combination is ok. Tests are included in the next patch to handle all special conditions. Fixes: 9bb00b2 ("bpf: Add kfunc bpf_rcu_read_lock/unlock()") Signed-off-by: Kumar Kartikeya Dwivedi <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 0b93631 commit 90d8c89

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,6 +1531,7 @@ struct bpf_prog_aux {
15311531
bool jits_use_priv_stack;
15321532
bool priv_stack_requested;
15331533
bool changes_pkt_data;
1534+
bool might_sleep;
15341535
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
15351536
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
15361537
struct bpf_arena *arena;

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ struct bpf_subprog_info {
667667
/* true if bpf_fastcall stack region is used by functions that can't be inlined */
668668
bool keep_fastcall_stack: 1;
669669
bool changes_pkt_data: 1;
670+
bool might_sleep: 1;
670671

671672
enum priv_stack_mode priv_stack_mode;
672673
u8 arg_cnt;

kernel/bpf/verifier.c

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10317,23 +10317,18 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1031710317
if (subprog_is_global(env, subprog)) {
1031810318
const char *sub_name = subprog_name(env, subprog);
1031910319

10320-
/* Only global subprogs cannot be called with a lock held. */
1032110320
if (env->cur_state->active_locks) {
1032210321
verbose(env, "global function calls are not allowed while holding a lock,\n"
1032310322
"use static function instead\n");
1032410323
return -EINVAL;
1032510324
}
1032610325

10327-
/* Only global subprogs cannot be called with preemption disabled. */
10328-
if (env->cur_state->active_preempt_locks) {
10329-
verbose(env, "global function calls are not allowed with preemption disabled,\n"
10330-
"use static function instead\n");
10331-
return -EINVAL;
10332-
}
10333-
10334-
if (env->cur_state->active_irq_id) {
10335-
verbose(env, "global function calls are not allowed with IRQs disabled,\n"
10336-
"use static function instead\n");
10326+
if (env->subprog_info[subprog].might_sleep &&
10327+
(env->cur_state->active_rcu_lock || env->cur_state->active_preempt_locks ||
10328+
env->cur_state->active_irq_id || !in_sleepable(env))) {
10329+
verbose(env, "global functions that may sleep are not allowed in non-sleepable context,\n"
10330+
"i.e., in a RCU/IRQ/preempt-disabled section, or in\n"
10331+
"a non-sleepable BPF program context\n");
1033710332
return -EINVAL;
1033810333
}
1033910334

@@ -16703,6 +16698,14 @@ static void mark_subprog_changes_pkt_data(struct bpf_verifier_env *env, int off)
1670316698
subprog->changes_pkt_data = true;
1670416699
}
1670516700

16701+
static void mark_subprog_might_sleep(struct bpf_verifier_env *env, int off)
16702+
{
16703+
struct bpf_subprog_info *subprog;
16704+
16705+
subprog = find_containing_subprog(env, off);
16706+
subprog->might_sleep = true;
16707+
}
16708+
1670616709
/* 't' is an index of a call-site.
1670716710
* 'w' is a callee entry point.
1670816711
* Eventually this function would be called when env->cfg.insn_state[w] == EXPLORED.
@@ -16716,6 +16719,7 @@ static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
1671616719
caller = find_containing_subprog(env, t);
1671716720
callee = find_containing_subprog(env, w);
1671816721
caller->changes_pkt_data |= callee->changes_pkt_data;
16722+
caller->might_sleep |= callee->might_sleep;
1671916723
}
1672016724

1672116725
/* non-recursive DFS pseudo code
@@ -17183,9 +17187,20 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
1718317187
mark_prune_point(env, t);
1718417188
mark_jmp_point(env, t);
1718517189
}
17186-
if (bpf_helper_call(insn) && bpf_helper_changes_pkt_data(insn->imm))
17187-
mark_subprog_changes_pkt_data(env, t);
17188-
if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
17190+
if (bpf_helper_call(insn)) {
17191+
const struct bpf_func_proto *fp;
17192+
17193+
ret = get_helper_proto(env, insn->imm, &fp);
17194+
/* If called in a non-sleepable context program will be
17195+
* rejected anyway, so we should end up with precise
17196+
* sleepable marks on subprogs, except for dead code
17197+
* elimination.
17198+
*/
17199+
if (ret == 0 && fp->might_sleep)
17200+
mark_subprog_might_sleep(env, t);
17201+
if (bpf_helper_changes_pkt_data(insn->imm))
17202+
mark_subprog_changes_pkt_data(env, t);
17203+
} else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
1718917204
struct bpf_kfunc_call_arg_meta meta;
1719017205

1719117206
ret = fetch_kfunc_meta(env, insn, &meta, NULL);
@@ -17204,6 +17219,13 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
1720417219
*/
1720517220
mark_force_checkpoint(env, t);
1720617221
}
17222+
/* Same as helpers, if called in a non-sleepable context
17223+
* program will be rejected anyway, so we should end up
17224+
* with precise sleepable marks on subprogs, except for
17225+
* dead code elimination.
17226+
*/
17227+
if (ret == 0 && is_kfunc_sleepable(&meta))
17228+
mark_subprog_might_sleep(env, t);
1720717229
}
1720817230
return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL);
1720917231

@@ -17320,6 +17342,7 @@ static int check_cfg(struct bpf_verifier_env *env)
1732017342
}
1732117343
ret = 0; /* cfg looks good */
1732217344
env->prog->aux->changes_pkt_data = env->subprog_info[0].changes_pkt_data;
17345+
env->prog->aux->might_sleep = env->subprog_info[0].might_sleep;
1732317346

1732417347
err_free:
1732517348
kvfree(insn_state);
@@ -20845,6 +20868,7 @@ static int jit_subprogs(struct bpf_verifier_env *env)
2084520868
func[i]->aux->tail_call_reachable = env->subprog_info[i].tail_call_reachable;
2084620869
func[i]->aux->exception_cb = env->subprog_info[i].is_exception_cb;
2084720870
func[i]->aux->changes_pkt_data = env->subprog_info[i].changes_pkt_data;
20871+
func[i]->aux->might_sleep = env->subprog_info[i].might_sleep;
2084820872
if (!i)
2084920873
func[i]->aux->exception_boundary = env->seen_exception;
2085020874
func[i] = bpf_int_jit_compile(func[i]);
@@ -22723,6 +22747,7 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
2272322747
if (tgt_prog) {
2272422748
struct bpf_prog_aux *aux = tgt_prog->aux;
2272522749
bool tgt_changes_pkt_data;
22750+
bool tgt_might_sleep;
2272622751

2272722752
if (bpf_prog_is_dev_bound(prog->aux) &&
2272822753
!bpf_prog_dev_bound_match(prog, tgt_prog)) {
@@ -22765,6 +22790,15 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
2276522790
"Extension program changes packet data, while original does not\n");
2276622791
return -EINVAL;
2276722792
}
22793+
22794+
tgt_might_sleep = aux->func
22795+
? aux->func[subprog]->aux->might_sleep
22796+
: aux->might_sleep;
22797+
if (prog->aux->might_sleep && !tgt_might_sleep) {
22798+
bpf_log(log,
22799+
"Extension program may sleep, while original does not\n");
22800+
return -EINVAL;
22801+
}
2276822802
}
2276922803
if (!tgt_prog->jited) {
2277022804
bpf_log(log, "Can attach to only JITed progs\n");

0 commit comments

Comments
 (0)