Skip to content

Commit 3547a61

Browse files
author
Alexei Starovoitov
committed
Merge branch 'update-kf_rcu_protected'
Kumar Kartikeya Dwivedi says: ==================== Update KF_RCU_PROTECTED Currently, KF_RCU_PROTECTED only applies to iterator APIs and that too in a convoluted fashion: the presence of this flag on the kfunc is used to set MEM_RCU in iterator type, and the lack of RCU protection results in an error only later, once next() or destroy() methods are invoked on the iterator. While there is no bug, this is certainly a bit unintuitive, and makes the enforcement of the flag iterator specific. In the interest of making this flag useful for other upcoming kfuncs, e.g. scx_bpf_cpu_curr() [0][1], add enforcement for invoking the kfunc in an RCU critical section in general. In addition to this, the aforementioned kfunc also needs to return an RCU protected pointer, which currently has no generic kfunc flag or annotation. Add such a flag as well while we are at it. [0]: https://lore.kernel.org/all/[email protected] [1]: https://lore.kernel.org/all/[email protected] Changelog: ---------- v2 -> v3 v2: https://lore.kernel.org/bpf/[email protected] * Add back lost hunk reworking documentation for KF_RCU_PROTECTED. v1 -> v2 v1: https://lore.kernel.org/bpf/[email protected] * Drop KF_RET_RCU and fold change into KF_RCU_PROTECTED. (Andrea, Alexei) * Update tests for non-struct pointer return values with KF_RCU_PROTECTED. ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 6ff4a0f + 8b788d6 commit 3547a61

File tree

7 files changed

+91
-4
lines changed

7 files changed

+91
-4
lines changed

Documentation/bpf/kfuncs.rst

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,26 @@ consider doing refcnt != 0 check, especially when returning a KF_ACQUIRE
335335
pointer. Note as well that a KF_ACQUIRE kfunc that is KF_RCU should very likely
336336
also be KF_RET_NULL.
337337

338+
2.4.8 KF_RCU_PROTECTED flag
339+
---------------------------
340+
341+
The KF_RCU_PROTECTED flag is used to indicate that the kfunc must be invoked in
342+
an RCU critical section. This is assumed by default in non-sleepable programs,
343+
and must be explicitly ensured by calling ``bpf_rcu_read_lock`` for sleepable
344+
ones.
345+
346+
If the kfunc returns a pointer value, this flag also enforces that the returned
347+
pointer is RCU protected, and can only be used while the RCU critical section is
348+
active.
349+
350+
The flag is distinct from the ``KF_RCU`` flag, which only ensures that its
351+
arguments are at least RCU protected pointers. This may transitively imply that
352+
RCU protection is ensured, but it does not work in cases of kfuncs which require
353+
RCU protection but do not take RCU protected arguments.
354+
338355
.. _KF_deprecated_flag:
339356

340-
2.4.8 KF_DEPRECATED flag
357+
2.4.9 KF_DEPRECATED flag
341358
------------------------
342359

343360
The KF_DEPRECATED flag is used for kfuncs which are scheduled to be

kernel/bpf/verifier.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13931,6 +13931,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1393113931
return -EACCES;
1393213932
}
1393313933

13934+
if (is_kfunc_rcu_protected(&meta) && !in_rcu_cs(env)) {
13935+
verbose(env, "kernel func %s requires RCU critical section protection\n", func_name);
13936+
return -EACCES;
13937+
}
13938+
1393413939
/* In case of release function, we get register number of refcounted
1393513940
* PTR_TO_BTF_ID in bpf_kfunc_arg_meta, do the release now.
1393613941
*/
@@ -14044,6 +14049,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1404414049
/* Ensures we don't access the memory after a release_reference() */
1404514050
if (meta.ref_obj_id)
1404614051
regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
14052+
14053+
if (is_kfunc_rcu_protected(&meta))
14054+
regs[BPF_REG_0].type |= MEM_RCU;
1404714055
} else {
1404814056
mark_reg_known_zero(env, regs, BPF_REG_0);
1404914057
regs[BPF_REG_0].btf = desc_btf;
@@ -14052,6 +14060,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
1405214060

1405314061
if (meta.func_id == special_kfunc_list[KF_bpf_get_kmem_cache])
1405414062
regs[BPF_REG_0].type |= PTR_UNTRUSTED;
14063+
else if (is_kfunc_rcu_protected(&meta))
14064+
regs[BPF_REG_0].type |= MEM_RCU;
1405514065

1405614066
if (is_iter_next_kfunc(&meta)) {
1405714067
struct bpf_reg_state *cur_iter;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ int BPF_PROG(use_css_iter_non_sleepable)
7373
}
7474

7575
SEC("lsm.s/socket_connect")
76-
__failure __msg("expected an RCU CS")
76+
__failure __msg("kernel func bpf_iter_css_new requires RCU critical section protection")
7777
int BPF_PROG(use_css_iter_sleepable_missing_rcu_lock)
7878
{
7979
u64 cgrp_id = bpf_get_current_cgroup_id();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void bpf_rcu_read_lock(void) __ksym;
1515
void bpf_rcu_read_unlock(void) __ksym;
1616

1717
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
18-
__failure __msg("expected an RCU CS when using bpf_iter_task_next")
18+
__failure __msg("kernel func bpf_iter_task_new requires RCU critical section protection")
1919
int BPF_PROG(iter_tasks_without_lock)
2020
{
2121
struct task_struct *pos;
@@ -27,7 +27,7 @@ int BPF_PROG(iter_tasks_without_lock)
2727
}
2828

2929
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
30-
__failure __msg("expected an RCU CS when using bpf_iter_css_next")
30+
__failure __msg("kernel func bpf_iter_css_new requires RCU critical section protection")
3131
int BPF_PROG(iter_css_without_lock)
3232
{
3333
u64 cg_id = bpf_get_current_cgroup_id();

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,49 @@ int iter_next_ptr_mem_not_trusted(const void *ctx)
123123
bpf_iter_num_destroy(&num_it);
124124
return 0;
125125
}
126+
127+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
128+
__failure __msg("kernel func bpf_kfunc_ret_rcu_test requires RCU critical section protection")
129+
int iter_ret_rcu_test_protected(const void *ctx)
130+
{
131+
struct task_struct *p;
132+
133+
p = bpf_kfunc_ret_rcu_test();
134+
return p->pid;
135+
}
136+
137+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
138+
__failure __msg("R1 type=rcu_ptr_or_null_ expected=")
139+
int iter_ret_rcu_test_type(const void *ctx)
140+
{
141+
struct task_struct *p;
142+
143+
bpf_rcu_read_lock();
144+
p = bpf_kfunc_ret_rcu_test();
145+
bpf_this_cpu_ptr(p);
146+
bpf_rcu_read_unlock();
147+
return 0;
148+
}
149+
150+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
151+
__failure __msg("kernel func bpf_kfunc_ret_rcu_test_nostruct requires RCU critical section protection")
152+
int iter_ret_rcu_test_protected_nostruct(const void *ctx)
153+
{
154+
void *p;
155+
156+
p = bpf_kfunc_ret_rcu_test_nostruct(4);
157+
return *(int *)p;
158+
}
159+
160+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
161+
__failure __msg("R1 type=rdonly_rcu_mem_or_null expected=")
162+
int iter_ret_rcu_test_type_nostruct(const void *ctx)
163+
{
164+
void *p;
165+
166+
bpf_rcu_read_lock();
167+
p = bpf_kfunc_ret_rcu_test_nostruct(4);
168+
bpf_this_cpu_ptr(p);
169+
bpf_rcu_read_unlock();
170+
return 0;
171+
}

tools/testing/selftests/bpf/test_kmods/bpf_testmod.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,16 @@ __bpf_kfunc void bpf_kfunc_rcu_task_test(struct task_struct *ptr)
218218
{
219219
}
220220

221+
__bpf_kfunc struct task_struct *bpf_kfunc_ret_rcu_test(void)
222+
{
223+
return NULL;
224+
}
225+
226+
__bpf_kfunc int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size)
227+
{
228+
return NULL;
229+
}
230+
221231
__bpf_kfunc struct bpf_testmod_ctx *
222232
bpf_testmod_ctx_create(int *err)
223233
{
@@ -623,6 +633,8 @@ BTF_ID_FLAGS(func, bpf_kfunc_trusted_vma_test, KF_TRUSTED_ARGS)
623633
BTF_ID_FLAGS(func, bpf_kfunc_trusted_task_test, KF_TRUSTED_ARGS)
624634
BTF_ID_FLAGS(func, bpf_kfunc_trusted_num_test, KF_TRUSTED_ARGS)
625635
BTF_ID_FLAGS(func, bpf_kfunc_rcu_task_test, KF_RCU)
636+
BTF_ID_FLAGS(func, bpf_kfunc_ret_rcu_test, KF_RET_NULL | KF_RCU_PROTECTED)
637+
BTF_ID_FLAGS(func, bpf_kfunc_ret_rcu_test_nostruct, KF_RET_NULL | KF_RCU_PROTECTED)
626638
BTF_ID_FLAGS(func, bpf_testmod_ctx_create, KF_ACQUIRE | KF_RET_NULL)
627639
BTF_ID_FLAGS(func, bpf_testmod_ctx_release, KF_RELEASE)
628640
BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_1)

tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ void bpf_kfunc_trusted_vma_test(struct vm_area_struct *ptr) __ksym;
158158
void bpf_kfunc_trusted_task_test(struct task_struct *ptr) __ksym;
159159
void bpf_kfunc_trusted_num_test(int *ptr) __ksym;
160160
void bpf_kfunc_rcu_task_test(struct task_struct *ptr) __ksym;
161+
struct task_struct *bpf_kfunc_ret_rcu_test(void) __ksym;
162+
int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
161163

162164
int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __ksym;
163165

0 commit comments

Comments
 (0)