Skip to content

Commit c427320

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-nested-rcu-critical-sections'
Puranjay Mohan says: ==================== bpf: Nested rcu critical sections v1: https://lore.kernel.org/bpf/[email protected]/ Changes in v1->v2: - Move the addition of new tests to a separate patch (Alexei) - Avoid incrementing active_rcu_locks at two places (Eduard) Support nested rcu critical sections by making the boolean flag active_rcu_lock a counter and use it to manage rcu critical section state. bpf_rcu_read_lock() increments this counter and bpf_rcu_read_unlock() decrements it, MEM_RCU -> PTR_UNTRUSTED transition happens when active_rcu_locks drops to 0. ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 8f7cf30 + cf49ec5 commit c427320

File tree

4 files changed

+66
-27
lines changed

4 files changed

+66
-27
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ struct bpf_verifier_state {
416416
u32 active_irq_id;
417417
u32 active_lock_id;
418418
void *active_lock_ptr;
419-
bool active_rcu_lock;
419+
u32 active_rcu_locks;
420420

421421
bool speculative;
422422
bool in_sleepable;

kernel/bpf/verifier.c

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,7 +1437,7 @@ static int copy_reference_state(struct bpf_verifier_state *dst, const struct bpf
14371437
dst->acquired_refs = src->acquired_refs;
14381438
dst->active_locks = src->active_locks;
14391439
dst->active_preempt_locks = src->active_preempt_locks;
1440-
dst->active_rcu_lock = src->active_rcu_lock;
1440+
dst->active_rcu_locks = src->active_rcu_locks;
14411441
dst->active_irq_id = src->active_irq_id;
14421442
dst->active_lock_id = src->active_lock_id;
14431443
dst->active_lock_ptr = src->active_lock_ptr;
@@ -5889,7 +5889,7 @@ static bool in_sleepable(struct bpf_verifier_env *env)
58895889
*/
58905890
static bool in_rcu_cs(struct bpf_verifier_env *env)
58915891
{
5892-
return env->cur_state->active_rcu_lock ||
5892+
return env->cur_state->active_rcu_locks ||
58935893
env->cur_state->active_locks ||
58945894
!in_sleepable(env);
58955895
}
@@ -10744,7 +10744,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1074410744
}
1074510745

1074610746
if (env->subprog_info[subprog].might_sleep &&
10747-
(env->cur_state->active_rcu_lock || env->cur_state->active_preempt_locks ||
10747+
(env->cur_state->active_rcu_locks || env->cur_state->active_preempt_locks ||
1074810748
env->cur_state->active_irq_id || !in_sleepable(env))) {
1074910749
verbose(env, "global functions that may sleep are not allowed in non-sleepable context,\n"
1075010750
"i.e., in a RCU/IRQ/preempt-disabled section, or in\n"
@@ -11327,7 +11327,7 @@ static int check_resource_leak(struct bpf_verifier_env *env, bool exception_exit
1132711327
return -EINVAL;
1132811328
}
1132911329

11330-
if (check_lock && env->cur_state->active_rcu_lock) {
11330+
if (check_lock && env->cur_state->active_rcu_locks) {
1133111331
verbose(env, "%s cannot be used inside bpf_rcu_read_lock-ed region\n", prefix);
1133211332
return -EINVAL;
1133311333
}
@@ -11465,7 +11465,7 @@ static int get_helper_proto(struct bpf_verifier_env *env, int func_id,
1146511465
/* Check if we're in a sleepable context. */
1146611466
static inline bool in_sleepable_context(struct bpf_verifier_env *env)
1146711467
{
11468-
return !env->cur_state->active_rcu_lock &&
11468+
return !env->cur_state->active_rcu_locks &&
1146911469
!env->cur_state->active_preempt_locks &&
1147011470
!env->cur_state->active_irq_id &&
1147111471
in_sleepable(env);
@@ -11531,7 +11531,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
1153111531
return err;
1153211532
}
1153311533

11534-
if (env->cur_state->active_rcu_lock) {
11534+
if (env->cur_state->active_rcu_locks) {
1153511535
if (fn->might_sleep) {
1153611536
verbose(env, "sleepable helper %s#%d in rcu_read_lock region\n",
1153711537
func_id_name(func_id), func_id);
@@ -14038,36 +14038,33 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1403814038
preempt_disable = is_kfunc_bpf_preempt_disable(&meta);
1403914039
preempt_enable = is_kfunc_bpf_preempt_enable(&meta);
1404014040

14041-
if (env->cur_state->active_rcu_lock) {
14041+
if (rcu_lock) {
14042+
env->cur_state->active_rcu_locks++;
14043+
} else if (rcu_unlock) {
1404214044
struct bpf_func_state *state;
1404314045
struct bpf_reg_state *reg;
1404414046
u32 clear_mask = (1 << STACK_SPILL) | (1 << STACK_ITER);
1404514047

14046-
if (in_rbtree_lock_required_cb(env) && (rcu_lock || rcu_unlock)) {
14047-
verbose(env, "Calling bpf_rcu_read_{lock,unlock} in unnecessary rbtree callback\n");
14048-
return -EACCES;
14049-
}
14050-
14051-
if (rcu_lock) {
14052-
verbose(env, "nested rcu read lock (kernel function %s)\n", func_name);
14048+
if (env->cur_state->active_rcu_locks == 0) {
14049+
verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name);
1405314050
return -EINVAL;
14054-
} else if (rcu_unlock) {
14051+
}
14052+
if (--env->cur_state->active_rcu_locks == 0) {
1405514053
bpf_for_each_reg_in_vstate_mask(env->cur_state, state, reg, clear_mask, ({
1405614054
if (reg->type & MEM_RCU) {
1405714055
reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL);
1405814056
reg->type |= PTR_UNTRUSTED;
1405914057
}
1406014058
}));
14061-
env->cur_state->active_rcu_lock = false;
14062-
} else if (sleepable) {
14063-
verbose(env, "kernel func %s is sleepable within rcu_read_lock region\n", func_name);
14064-
return -EACCES;
1406514059
}
14066-
} else if (rcu_lock) {
14067-
env->cur_state->active_rcu_lock = true;
14068-
} else if (rcu_unlock) {
14069-
verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name);
14070-
return -EINVAL;
14060+
} else if (sleepable && env->cur_state->active_rcu_locks) {
14061+
verbose(env, "kernel func %s is sleepable within rcu_read_lock region\n", func_name);
14062+
return -EACCES;
14063+
}
14064+
14065+
if (in_rbtree_lock_required_cb(env) && (rcu_lock || rcu_unlock)) {
14066+
verbose(env, "Calling bpf_rcu_read_{lock,unlock} in unnecessary rbtree callback\n");
14067+
return -EACCES;
1407114068
}
1407214069

1407314070
if (env->cur_state->active_preempt_locks) {
@@ -19387,7 +19384,7 @@ static bool refsafe(struct bpf_verifier_state *old, struct bpf_verifier_state *c
1938719384
if (old->active_preempt_locks != cur->active_preempt_locks)
1938819385
return false;
1938919386

19390-
if (old->active_rcu_lock != cur->active_rcu_lock)
19387+
if (old->active_rcu_locks != cur->active_rcu_locks)
1939119388
return false;
1939219389

1939319390
if (!check_ids(old->active_irq_id, cur->active_irq_id, idmap))

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static void test_success(void)
2828
bpf_program__set_autoload(skel->progs.two_regions, true);
2929
bpf_program__set_autoload(skel->progs.non_sleepable_1, true);
3030
bpf_program__set_autoload(skel->progs.non_sleepable_2, true);
31+
bpf_program__set_autoload(skel->progs.nested_rcu_region, true);
3132
bpf_program__set_autoload(skel->progs.task_trusted_non_rcuptr, true);
3233
bpf_program__set_autoload(skel->progs.rcu_read_lock_subprog, true);
3334
bpf_program__set_autoload(skel->progs.rcu_read_lock_global_subprog, true);
@@ -78,7 +79,8 @@ static const char * const inproper_region_tests[] = {
7879
"non_sleepable_rcu_mismatch",
7980
"inproper_sleepable_helper",
8081
"inproper_sleepable_kfunc",
81-
"nested_rcu_region",
82+
"nested_rcu_region_unbalanced_1",
83+
"nested_rcu_region_unbalanced_2",
8284
"rcu_read_lock_global_subprog_lock",
8385
"rcu_read_lock_global_subprog_unlock",
8486
"rcu_read_lock_sleepable_helper_global_subprog",

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,46 @@ int nested_rcu_region(void *ctx)
278278
return 0;
279279
}
280280

281+
SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
282+
int nested_rcu_region_unbalanced_1(void *ctx)
283+
{
284+
struct task_struct *task, *real_parent;
285+
286+
/* nested rcu read lock regions */
287+
task = bpf_get_current_task_btf();
288+
bpf_rcu_read_lock();
289+
bpf_rcu_read_lock();
290+
real_parent = task->real_parent;
291+
if (!real_parent)
292+
goto out;
293+
(void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
294+
out:
295+
bpf_rcu_read_unlock();
296+
bpf_rcu_read_unlock();
297+
bpf_rcu_read_unlock();
298+
return 0;
299+
}
300+
301+
SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
302+
int nested_rcu_region_unbalanced_2(void *ctx)
303+
{
304+
struct task_struct *task, *real_parent;
305+
306+
/* nested rcu read lock regions */
307+
task = bpf_get_current_task_btf();
308+
bpf_rcu_read_lock();
309+
bpf_rcu_read_lock();
310+
bpf_rcu_read_lock();
311+
real_parent = task->real_parent;
312+
if (!real_parent)
313+
goto out;
314+
(void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
315+
out:
316+
bpf_rcu_read_unlock();
317+
bpf_rcu_read_unlock();
318+
return 0;
319+
}
320+
281321
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
282322
int task_trusted_non_rcuptr(void *ctx)
283323
{

0 commit comments

Comments
 (0)