BPF List
 help / color / mirror / Atom feed
* [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin
@ 2025-01-01 20:37 Emil Tsalapatis
  2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis
  2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis
  0 siblings, 2 replies; 9+ messages in thread
From: Emil Tsalapatis @ 2025-01-01 20:37 UTC (permalink / raw)
  To: bpf; +Cc: ast, daniel, andrii, eddyz87, Emil Tsalapatis

From: Emil Tsalapatis (Meta) <emil@etsalapatis.com>

In BPF programs, kfunc calls while holding a lock are not allowed
because kfuncs may sleep by default. The exception to this rule are the
functions in special_kfunc_list, which are guaranteed to not sleep. The
bpf_iter_num_* functions used by the bpf_for and bpf_repeat macros make
no function calls themselves, and as such are guaranteed to not sleep.
Add them to special_kfunc_list to allow them within BPF spinlock
critical sections.

Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>

Emil Tsalapatis (2):
  bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  selftests/bpf: test bpf_for within spin lock section

 kernel/bpf/verifier.c                         | 20 +++++++++++++-
 .../selftests/bpf/progs/verifier_spin_lock.c  | 26 +++++++++++++++++++
 2 files changed, 45 insertions(+), 1 deletion(-)

-- 
2.47.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  2025-01-01 20:37 [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin Emil Tsalapatis
@ 2025-01-01 20:37 ` Emil Tsalapatis
  2025-01-02 18:02   ` Eduard Zingerman
  2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis
  1 sibling, 1 reply; 9+ messages in thread
From: Emil Tsalapatis @ 2025-01-01 20:37 UTC (permalink / raw)
  To: bpf; +Cc: ast, daniel, andrii, eddyz87, Emil Tsalapatis

 Add the bpf_iter_num_* kfuncs called by bpf_for in special_kfunc_list,
 and allow the calls even while holding a spin lock.

Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
---
 kernel/bpf/verifier.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d77abb87ffb1..f209021914b1 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -11690,6 +11690,9 @@ enum special_kfunc_type {
 	KF_bpf_get_kmem_cache,
 	KF_bpf_local_irq_save,
 	KF_bpf_local_irq_restore,
+	KF_bpf_iter_num_new,
+	KF_bpf_iter_num_next,
+	KF_bpf_iter_num_destroy,
 };
 
 BTF_SET_START(special_kfunc_set)
@@ -11765,6 +11768,9 @@ BTF_ID_UNUSED
 BTF_ID(func, bpf_get_kmem_cache)
 BTF_ID(func, bpf_local_irq_save)
 BTF_ID(func, bpf_local_irq_restore)
+BTF_ID(func, bpf_iter_num_new)
+BTF_ID(func, bpf_iter_num_next)
+BTF_ID(func, bpf_iter_num_destroy)
 
 static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta)
 {
@@ -12151,12 +12157,24 @@ static bool is_bpf_rbtree_api_kfunc(u32 btf_id)
 	       btf_id == special_kfunc_list[KF_bpf_rbtree_first];
 }
 
+static bool is_bpf_loop_api_kfunc(u32 btf_id)
+{
+	return btf_id == special_kfunc_list[KF_bpf_iter_num_new] ||
+	       btf_id == special_kfunc_list[KF_bpf_iter_num_next] ||
+	       btf_id == special_kfunc_list[KF_bpf_iter_num_destroy];
+}
+
 static bool is_bpf_graph_api_kfunc(u32 btf_id)
 {
 	return is_bpf_list_api_kfunc(btf_id) || is_bpf_rbtree_api_kfunc(btf_id) ||
 	       btf_id == special_kfunc_list[KF_bpf_refcount_acquire_impl];
 }
 
+static bool kfunc_spin_allowed(u32 btf_id)
+{
+	return is_bpf_graph_api_kfunc(btf_id) || is_bpf_loop_api_kfunc(btf_id);
+}
+
 static bool is_sync_callback_calling_kfunc(u32 btf_id)
 {
 	return btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl];
@@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env)
 				if (env->cur_state->active_locks) {
 					if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) ||
 					    (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
-					     (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) {
+					     (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
 						verbose(env, "function calls are not allowed while holding a lock\n");
 						return -EINVAL;
 					}
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section
  2025-01-01 20:37 [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin Emil Tsalapatis
  2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis
@ 2025-01-01 20:37 ` Emil Tsalapatis
  2025-01-02 18:05   ` Eduard Zingerman
  1 sibling, 1 reply; 9+ messages in thread
From: Emil Tsalapatis @ 2025-01-01 20:37 UTC (permalink / raw)
  To: bpf; +Cc: ast, daniel, andrii, eddyz87, Emil Tsalapatis

Add a selftest to ensure BPF for loops within critical sections are
accepted by the verifier.

Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
---
 .../selftests/bpf/progs/verifier_spin_lock.c  | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/tools/testing/selftests/bpf/progs/verifier_spin_lock.c b/tools/testing/selftests/bpf/progs/verifier_spin_lock.c
index 25599eac9a70..d9d7b05cf6d2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_spin_lock.c
+++ b/tools/testing/selftests/bpf/progs/verifier_spin_lock.c
@@ -530,4 +530,30 @@ l1_%=:	exit;						\
 	: __clobber_all);
 }
 
+SEC("tc")
+__description("spin_lock: loop within a locked region")
+__success __failure_unpriv __msg_unpriv("")
+__retval(0)
+int bpf_loop_inside_locked_region(void)
+{
+	const int zero = 0;
+	struct val *val;
+	int i, j = 0;
+
+	val = bpf_map_lookup_elem(&map_spin_lock, &zero);
+	if (!val)
+		return -1;
+
+	bpf_spin_lock(&val->l);
+	bpf_for(i, 0, 10) {
+		j++;
+		/* Silence "unused variable" warnings. */
+		if (j == 10)
+			break;
+	}
+	bpf_spin_unlock(&val->l);
+
+	return 0;
+}
+
 char _license[] SEC("license") = "GPL";
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis
@ 2025-01-02 18:02   ` Eduard Zingerman
  2025-01-04 19:25     ` Emil Tsalapatis
  0 siblings, 1 reply; 9+ messages in thread
From: Eduard Zingerman @ 2025-01-02 18:02 UTC (permalink / raw)
  To: Emil Tsalapatis, bpf; +Cc: ast, daniel, andrii

On Wed, 2025-01-01 at 15:37 -0500, Emil Tsalapatis wrote:
>  Add the bpf_iter_num_* kfuncs called by bpf_for in special_kfunc_list,
>  and allow the calls even while holding a spin lock.
> 
> Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
> ---

Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>

[...]

> @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env)
>  				if (env->cur_state->active_locks) {
>  					if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) ||
>  					    (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
> -					     (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) {
> +					     (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
>  						verbose(env, "function calls are not allowed while holding a lock\n");
>  						return -EINVAL;
>  					}


Nit: technically, 'bpf_loop' is a helper function independent of iter_num API.
     I suggest to change the name to is_bpf_iter_num_api_kfunc.
     Also, if we decide that loops are ok with spin locks,
     the condition above should be adjusted to allow calls to bpf_loop,
     e.g. to make the following test work:

--- 8< -------------------------------------
static int loop_cb(__u64 index, void *ctx)
{
	return 0;
}

SEC("tc")
__success __failure_unpriv __msg_unpriv("")
__retval(0)
int bpf_loop_inside_locked_region2(void)
{
	const int zero = 0;
	struct val *val;

	val = bpf_map_lookup_elem(&map_spin_lock, &zero);
	if (!val)
		return -1;

	bpf_spin_lock(&val->l);
	bpf_loop(10, loop_cb, NULL, 0);
	bpf_spin_unlock(&val->l);

	return 0;
}
------------------------------------- >8 ---




^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section
  2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis
@ 2025-01-02 18:05   ` Eduard Zingerman
  0 siblings, 0 replies; 9+ messages in thread
From: Eduard Zingerman @ 2025-01-02 18:05 UTC (permalink / raw)
  To: Emil Tsalapatis, bpf; +Cc: ast, daniel, andrii

On Wed, 2025-01-01 at 15:37 -0500, Emil Tsalapatis wrote:
> Add a selftest to ensure BPF for loops within critical sections are
> accepted by the verifier.
> 
> Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
> ---

Acked-by: Eduard Zingerman <eddyz87@gmail.com>

[...]


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  2025-01-02 18:02   ` Eduard Zingerman
@ 2025-01-04 19:25     ` Emil Tsalapatis
  2025-01-05  0:11       ` Eduard Zingerman
  0 siblings, 1 reply; 9+ messages in thread
From: Emil Tsalapatis @ 2025-01-04 19:25 UTC (permalink / raw)
  To: Eduard Zingerman; +Cc: bpf, ast, daniel, andrii

On Thu, Jan 2, 2025 at 1:02 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Wed, 2025-01-01 at 15:37 -0500, Emil Tsalapatis wrote:
> >  Add the bpf_iter_num_* kfuncs called by bpf_for in special_kfunc_list,
> >  and allow the calls even while holding a spin lock.
> >
> > Signed-off-by: Emil Tsalapatis (Meta) <emil@etsalapatis.com>
> > ---
>
> Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
>
> [...]
>
> > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env)
> >                               if (env->cur_state->active_locks) {
> >                                       if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) ||
> >                                           (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
> > -                                          (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) {
> > +                                          (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
> >                                               verbose(env, "function calls are not allowed while holding a lock\n");
> >                                               return -EINVAL;
> >                                       }
>
>
> Nit: technically, 'bpf_loop' is a helper function independent of iter_num API.
>      I suggest to change the name to is_bpf_iter_num_api_kfunc.
>      Also, if we decide that loops are ok with spin locks,
>      the condition above should be adjusted to allow calls to bpf_loop,
>      e.g. to make the following test work:
>

(Sorry for the duplicate, accidentally didn't send the email in plaintext)

Will do, bpf_iter_num_api_kfunc is more reasonable. For bpf_loops
AFAICT we would need to ensure the callback cannot sleep,
which would need extra checks/changes to the verifier compared to
bpf_for. IMO we can deal with it in a separate patch if we think
allowing it is a good idea.

> --- 8< -------------------------------------
> static int loop_cb(__u64 index, void *ctx)
> {
>         return 0;
> }
>
> SEC("tc")
> __success __failure_unpriv __msg_unpriv("")
> __retval(0)
> int bpf_loop_inside_locked_region2(void)
> {
>         const int zero = 0;
>         struct val *val;
>
>         val = bpf_map_lookup_elem(&map_spin_lock, &zero);
>         if (!val)
>                 return -1;
>
>         bpf_spin_lock(&val->l);
>         bpf_loop(10, loop_cb, NULL, 0);
>         bpf_spin_unlock(&val->l);
>
>         return 0;
> }
> ------------------------------------- >8 ---
>
>
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  2025-01-04 19:25     ` Emil Tsalapatis
@ 2025-01-05  0:11       ` Eduard Zingerman
  2025-01-06 14:56         ` Emil Tsalapatis
  0 siblings, 1 reply; 9+ messages in thread
From: Eduard Zingerman @ 2025-01-05  0:11 UTC (permalink / raw)
  To: Emil Tsalapatis; +Cc: bpf, ast, daniel, andrii

On Sat, 2025-01-04 at 14:25 -0500, Emil Tsalapatis wrote:

[...]

> > > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env)
> > >                               if (env->cur_state->active_locks) {
> > >                                       if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) ||
> > >                                           (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
> > > -                                          (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) {
> > > +                                          (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
> > >                                               verbose(env, "function calls are not allowed while holding a lock\n");
> > >                                               return -EINVAL;
> > >                                       }
> > 
> > 
> > Nit: technically, 'bpf_loop' is a helper function independent of iter_num API.
> >      I suggest to change the name to is_bpf_iter_num_api_kfunc.
> >      Also, if we decide that loops are ok with spin locks,
> >      the condition above should be adjusted to allow calls to bpf_loop,
> >      e.g. to make the following test work:
> > 
> 
> (Sorry for the duplicate, accidentally didn't send the email in plaintext)
> 
> Will do, bpf_iter_num_api_kfunc is more reasonable. For bpf_loops
> AFAICT we would need to ensure the callback cannot sleep,
> which would need extra checks/changes to the verifier compared to
> bpf_for. IMO we can deal with it in a separate patch if we think
> allowing it is a good idea.

Not really, callbacks are verified "in-line". When a function call to
a function calling synchronous callback is verified, verifier steps
into callback body some number of times. If a sleeping call would
be discovered during callback function verification, verifier would
see that spin lock is currently taken and report error. So, this is
really just a check for particular helper call.

[...]


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  2025-01-05  0:11       ` Eduard Zingerman
@ 2025-01-06 14:56         ` Emil Tsalapatis
  2025-01-06 18:59           ` Alexei Starovoitov
  0 siblings, 1 reply; 9+ messages in thread
From: Emil Tsalapatis @ 2025-01-06 14:56 UTC (permalink / raw)
  To: Eduard Zingerman; +Cc: bpf, ast, daniel, andrii

I see, thank you for the feedback. in that case I will send another
version that handles bpf_loop.

On Sat, Jan 4, 2025 at 7:11 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Sat, 2025-01-04 at 14:25 -0500, Emil Tsalapatis wrote:
>
> [...]
>
> > > > @@ -19048,7 +19066,7 @@ static int do_check(struct bpf_verifier_env *env)
> > > >                               if (env->cur_state->active_locks) {
> > > >                                       if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) ||
> > > >                                           (insn->src_reg == BPF_PSEUDO_KFUNC_CALL &&
> > > > -                                          (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) {
> > > > +                                          (insn->off != 0 || !kfunc_spin_allowed(insn->imm)))) {
> > > >                                               verbose(env, "function calls are not allowed while holding a lock\n");
> > > >                                               return -EINVAL;
> > > >                                       }
> > >
> > >
> > > Nit: technically, 'bpf_loop' is a helper function independent of iter_num API.
> > >      I suggest to change the name to is_bpf_iter_num_api_kfunc.
> > >      Also, if we decide that loops are ok with spin locks,
> > >      the condition above should be adjusted to allow calls to bpf_loop,
> > >      e.g. to make the following test work:
> > >
> >
> > (Sorry for the duplicate, accidentally didn't send the email in plaintext)
> >
> > Will do, bpf_iter_num_api_kfunc is more reasonable. For bpf_loops
> > AFAICT we would need to ensure the callback cannot sleep,
> > which would need extra checks/changes to the verifier compared to
> > bpf_for. IMO we can deal with it in a separate patch if we think
> > allowing it is a good idea.
>
> Not really, callbacks are verified "in-line". When a function call to
> a function calling synchronous callback is verified, verifier steps
> into callback body some number of times. If a sleeping call would
> be discovered during callback function verification, verifier would
> see that spin lock is currently taken and report error. So, this is
> really just a check for particular helper call.
>
> [...]
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock
  2025-01-06 14:56         ` Emil Tsalapatis
@ 2025-01-06 18:59           ` Alexei Starovoitov
  0 siblings, 0 replies; 9+ messages in thread
From: Alexei Starovoitov @ 2025-01-06 18:59 UTC (permalink / raw)
  To: Emil Tsalapatis
  Cc: Eduard Zingerman, bpf, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko

On Mon, Jan 6, 2025 at 7:04 AM Emil Tsalapatis <emil@etsalapatis.com> wrote:
>
> I see, thank you for the feedback. in that case I will send another
> version that handles bpf_loop.

I think that can be a separate follow up.
I'll apply the v2 as-is.

Also pls don't top post. This mailing list preferes inline replies.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-01-06 18:59 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-01 20:37 [PATCH 0/2] bpf: Allow bpf_for/bpf_repeat while holding spin Emil Tsalapatis
2025-01-01 20:37 ` [PATCH 1/2] bpf: Allow bpf_for/bpf_repeat calls while holding a spinlock Emil Tsalapatis
2025-01-02 18:02   ` Eduard Zingerman
2025-01-04 19:25     ` Emil Tsalapatis
2025-01-05  0:11       ` Eduard Zingerman
2025-01-06 14:56         ` Emil Tsalapatis
2025-01-06 18:59           ` Alexei Starovoitov
2025-01-01 20:37 ` [PATCH 2/2] selftests/bpf: test bpf_for within spin lock section Emil Tsalapatis
2025-01-02 18:05   ` Eduard Zingerman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox