Skip to content

Commit 53415f6

Browse files
author
Alexei Starovoitov
committed
Merge branch 'global-subprogs-in-rcu-preempt-irq-disabled-sections'
Kumar Kartikeya Dwivedi says: ==================== Global subprogs in RCU/{preempt,irq}-disabled sections Small change to allow non-sleepable global subprogs in RCU, preempt-disabled, and irq-disabled sections. For now, we don't lift the limitation for locks as it requires more analysis, and will do this one resilient spin locks land. This surfaced a bug where sleepable global subprogs were allowed in RCU read sections, that has been fixed. Tests have been added to cover various cases. Changelog: ---------- v2 -> v3 v2: https://lore.kernel.org/bpf/[email protected] * Fix broken to_be_replaced argument in the selftest. * Adjust selftest program type. v1 -> v2 v1: https://lore.kernel.org/bpf/[email protected] * Rename subprog_info[i].sleepable to might_sleep, which more accurately reflects the nature of the bit. 'sleepable' means whether a given context is allowed to, while might_sleep captures if it does. * Disallow extensions that might sleep to attach to targets that don't sleep, since they'd be permitted to be called in atomic contexts. (Eduard) * Add tests for mixing non-sleepable and sleepable global function calls, and extensions attaching to non-sleepable global functions. (Eduard) * Rename changes_pkt_data -> summarization ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 0b93631 + ce9add7 commit 53415f6

File tree

14 files changed

+558
-163
lines changed

14 files changed

+558
-163
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");

tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c

Lines changed: 0 additions & 107 deletions
This file was deleted.

tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ static const char * const inproper_region_tests[] = {
8181
"nested_rcu_region",
8282
"rcu_read_lock_global_subprog_lock",
8383
"rcu_read_lock_global_subprog_unlock",
84+
"rcu_read_lock_sleepable_helper_global_subprog",
85+
"rcu_read_lock_sleepable_kfunc_global_subprog",
86+
"rcu_read_lock_sleepable_global_subprog_indirect",
8487
};
8588

8689
static void test_inproper_region(void)

tools/testing/selftests/bpf/prog_tests/spin_lock.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ static struct {
5050
{ "lock_id_mismatch_innermapval_mapval", "bpf_spin_unlock of different lock" },
5151
{ "lock_global_subprog_call1", "global function calls are not allowed while holding a lock" },
5252
{ "lock_global_subprog_call2", "global function calls are not allowed while holding a lock" },
53+
{ "lock_global_sleepable_helper_subprog", "global function calls are not allowed while holding a lock" },
54+
{ "lock_global_sleepable_kfunc_subprog", "global function calls are not allowed while holding a lock" },
55+
{ "lock_global_sleepable_subprog_indirect", "global function calls are not allowed while holding a lock" },
5356
};
5457

5558
static int match_regex(const char *pattern, const char *string)

0 commit comments

Comments
 (0)