diff mbox series

[bpf-next,01/16] bpf: Allow kfuncs return 'void *'

Message ID 20240206220441.38311-2-alexei.starovoitov@gmail.com (mailing list archive)
State New
Headers show
Series bpf: Introduce BPF arena. | expand

Commit Message

Alexei Starovoitov Feb. 6, 2024, 10:04 p.m. UTC
From: Alexei Starovoitov <ast@kernel.org>

Recognize return of 'void *' from kfunc as returning unknown scalar.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
 kernel/bpf/verifier.c | 3 +++
 1 file changed, 3 insertions(+)

Comments

Andrii Nakryiko Feb. 8, 2024, 7:40 p.m. UTC | #1
On Tue, Feb 6, 2024 at 2:04 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> From: Alexei Starovoitov <ast@kernel.org>
>
> Recognize return of 'void *' from kfunc as returning unknown scalar.
>
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
> ---
>  kernel/bpf/verifier.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index ddaf09db1175..d9c2dbb3939f 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12353,6 +12353,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>                                         meta.func_name);
>                                 return -EFAULT;
>                         }
> +               } else if (btf_type_is_void(ptr_type)) {
> +                       /* kfunc returning 'void *' is equivalent to returning scalar */
> +                       mark_reg_unknown(env, regs, BPF_REG_0);

Acked-by: Andrii Nakryiko <andrii@kernel.org>

I think we should do a similar extension when passing `void *` into
global funcs. It's best to treat it as SCALAR instead of rejecting it
because we can't calculate the size. Currently users in practice just
have to define it as `uintptr_t` and then cast (or create static
wrappers doing the casting). Anyways, my point is that it makes sense
to treat `void *` as non-pointer.

>                 } else if (!__btf_type_is_struct(ptr_type)) {
>                         if (!meta.r0_size) {
>                                 __u32 sz;
> --
> 2.34.1
>
Alexei Starovoitov Feb. 9, 2024, 12:09 a.m. UTC | #2
On Thu, Feb 8, 2024 at 11:40 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Tue, Feb 6, 2024 at 2:04 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > From: Alexei Starovoitov <ast@kernel.org>
> >
> > Recognize return of 'void *' from kfunc as returning unknown scalar.
> >
> > Signed-off-by: Alexei Starovoitov <ast@kernel.org>
> > ---
> >  kernel/bpf/verifier.c | 3 +++
> >  1 file changed, 3 insertions(+)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index ddaf09db1175..d9c2dbb3939f 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -12353,6 +12353,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> >                                         meta.func_name);
> >                                 return -EFAULT;
> >                         }
> > +               } else if (btf_type_is_void(ptr_type)) {
> > +                       /* kfunc returning 'void *' is equivalent to returning scalar */
> > +                       mark_reg_unknown(env, regs, BPF_REG_0);
>
> Acked-by: Andrii Nakryiko <andrii@kernel.org>
>
> I think we should do a similar extension when passing `void *` into
> global funcs. It's best to treat it as SCALAR instead of rejecting it
> because we can't calculate the size. Currently users in practice just
> have to define it as `uintptr_t` and then cast (or create static
> wrappers doing the casting). Anyways, my point is that it makes sense
> to treat `void *` as non-pointer.

Makes sense. Will add it to my todo list.

On that note I've been thinking how to get rid of __arg_arena
that I'm adding in this series.

How about the following algorithm?
do_check_main() sees that scalar or ptr_to_arena is passed
into global subprog that has BTF 'struct foo *'
and today would require ptr_to_mem.
Instead of rejecting the prog the verifier would override
(only once and in one direction)
that arg of that global func from ptr_to_mem into scalar.
And will proceed as usual.
do_check_common() of that global subprog will pick up scalar
for that arg, since args are cached.
And verification will proceed successfully without special __arg_arena
.
David Vernet Feb. 9, 2024, 4:06 p.m. UTC | #3
On Tue, Feb 06, 2024 at 02:04:26PM -0800, Alexei Starovoitov wrote:
> From: Alexei Starovoitov <ast@kernel.org>
> 
> Recognize return of 'void *' from kfunc as returning unknown scalar.
> 
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>

Acked-by: David Vernet <void@manifault.com>
Andrii Nakryiko Feb. 9, 2024, 7:09 p.m. UTC | #4
On Thu, Feb 8, 2024 at 4:09 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Thu, Feb 8, 2024 at 11:40 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Tue, Feb 6, 2024 at 2:04 PM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > >
> > > From: Alexei Starovoitov <ast@kernel.org>
> > >
> > > Recognize return of 'void *' from kfunc as returning unknown scalar.
> > >
> > > Signed-off-by: Alexei Starovoitov <ast@kernel.org>
> > > ---
> > >  kernel/bpf/verifier.c | 3 +++
> > >  1 file changed, 3 insertions(+)
> > >
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index ddaf09db1175..d9c2dbb3939f 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -12353,6 +12353,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > >                                         meta.func_name);
> > >                                 return -EFAULT;
> > >                         }
> > > +               } else if (btf_type_is_void(ptr_type)) {
> > > +                       /* kfunc returning 'void *' is equivalent to returning scalar */
> > > +                       mark_reg_unknown(env, regs, BPF_REG_0);
> >
> > Acked-by: Andrii Nakryiko <andrii@kernel.org>
> >
> > I think we should do a similar extension when passing `void *` into
> > global funcs. It's best to treat it as SCALAR instead of rejecting it
> > because we can't calculate the size. Currently users in practice just
> > have to define it as `uintptr_t` and then cast (or create static
> > wrappers doing the casting). Anyways, my point is that it makes sense
> > to treat `void *` as non-pointer.
>
> Makes sense. Will add it to my todo list.
>
> On that note I've been thinking how to get rid of __arg_arena
> that I'm adding in this series.
>
> How about the following algorithm?
> do_check_main() sees that scalar or ptr_to_arena is passed
> into global subprog that has BTF 'struct foo *'
> and today would require ptr_to_mem.
> Instead of rejecting the prog the verifier would override
> (only once and in one direction)
> that arg of that global func from ptr_to_mem into scalar.
> And will proceed as usual.
> do_check_common() of that global subprog will pick up scalar
> for that arg, since args are cached.
> And verification will proceed successfully without special __arg_arena
> .

Can we pass PTR_TO_MEM (e.g., map value pointer) to something that is
expecting PTR_TO_ARENA? Because there are few problems with the above
algorithm, I think.

First, this check won't be just in do_check_main(), the same global
function can be called from another function.

And second, what if you have the first few calls that pass PTR_TO_MEM.
Verifier sees that, allows it, assumes global func will take
PTR_TO_MEM. Then we get to a call that passes PTR_TO_ARENA or scalar,
we change the argument expectation to be __arg_arena-like and
subsequent checks will assume arena stuff. But the first few calls
already assumed correctness based on PTR_TO_MEM.


In short, it seems like this introduces more subtleness and
potentially unexpected interactions. I don't really see explicit
__arg_arena as a bad thing, I find that explicit annotations for
"special things" help in practice as they bring specialness into
attention. And also allow people to ask/google more specific
questions.
Alexei Starovoitov Feb. 10, 2024, 2:32 a.m. UTC | #5
On Fri, Feb 9, 2024 at 11:09 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Thu, Feb 8, 2024 at 4:09 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > On Thu, Feb 8, 2024 at 11:40 AM Andrii Nakryiko
> > <andrii.nakryiko@gmail.com> wrote:
> > >
> > > On Tue, Feb 6, 2024 at 2:04 PM Alexei Starovoitov
> > > <alexei.starovoitov@gmail.com> wrote:
> > > >
> > > > From: Alexei Starovoitov <ast@kernel.org>
> > > >
> > > > Recognize return of 'void *' from kfunc as returning unknown scalar.
> > > >
> > > > Signed-off-by: Alexei Starovoitov <ast@kernel.org>
> > > > ---
> > > >  kernel/bpf/verifier.c | 3 +++
> > > >  1 file changed, 3 insertions(+)
> > > >
> > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > > index ddaf09db1175..d9c2dbb3939f 100644
> > > > --- a/kernel/bpf/verifier.c
> > > > +++ b/kernel/bpf/verifier.c
> > > > @@ -12353,6 +12353,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > > >                                         meta.func_name);
> > > >                                 return -EFAULT;
> > > >                         }
> > > > +               } else if (btf_type_is_void(ptr_type)) {
> > > > +                       /* kfunc returning 'void *' is equivalent to returning scalar */
> > > > +                       mark_reg_unknown(env, regs, BPF_REG_0);
> > >
> > > Acked-by: Andrii Nakryiko <andrii@kernel.org>
> > >
> > > I think we should do a similar extension when passing `void *` into
> > > global funcs. It's best to treat it as SCALAR instead of rejecting it
> > > because we can't calculate the size. Currently users in practice just
> > > have to define it as `uintptr_t` and then cast (or create static
> > > wrappers doing the casting). Anyways, my point is that it makes sense
> > > to treat `void *` as non-pointer.
> >
> > Makes sense. Will add it to my todo list.
> >
> > On that note I've been thinking how to get rid of __arg_arena
> > that I'm adding in this series.
> >
> > How about the following algorithm?
> > do_check_main() sees that scalar or ptr_to_arena is passed
> > into global subprog that has BTF 'struct foo *'
> > and today would require ptr_to_mem.
> > Instead of rejecting the prog the verifier would override
> > (only once and in one direction)
> > that arg of that global func from ptr_to_mem into scalar.
> > And will proceed as usual.
> > do_check_common() of that global subprog will pick up scalar
> > for that arg, since args are cached.
> > And verification will proceed successfully without special __arg_arena
> > .
>
> Can we pass PTR_TO_MEM (e.g., map value pointer) to something that is
> expecting PTR_TO_ARENA? Because there are few problems with the above
> algorithm, I think.

The patch 10 allows only ptr_to_arena and scalar to be passed in,
but passing ptr_to_mem is safe too. It won't crash the kernel,
but it won't do what user might expect.
Hence it's disabled.

> First, this check won't be just in do_check_main(), the same global
> function can be called from another function.

that shouldn't matter.

> And second, what if you have the first few calls that pass PTR_TO_MEM.
> Verifier sees that, allows it, assumes global func will take
> PTR_TO_MEM. Then we get to a call that passes PTR_TO_ARENA or scalar,
> we change the argument expectation to be __arg_arena-like and
> subsequent checks will assume arena stuff. But the first few calls
> already assumed correctness based on PTR_TO_MEM.

I think that would the issue only if we call that global func
with ptr_to_mem and then went at processed the body of it
with ptr_to_mem and later discovered another call site that
passes scalar.
Such bug can be accounted for.

> In short, it seems like this introduces more subtleness and
> potentially unexpected interactions. I don't really see explicit
> __arg_arena as a bad thing, I find that explicit annotations for
> "special things" help in practice as they bring specialness into
> attention. And also allow people to ask/google more specific
> questions.

In general I agree that __arg is a useful indication.
I've been writing arena enabled bpf progs and found this __arg_arena
quite annoying to add when converting static to global func.
I know that globals are verified differently, but it feels
we can do better with arena pointers.

Anyway I'll proceed with the existing __arg_arean approach and
put this "smart" detection of arena pointers on back burner for now.
diff mbox series

Patch

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index ddaf09db1175..d9c2dbb3939f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12353,6 +12353,9 @@  static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 					meta.func_name);
 				return -EFAULT;
 			}
+		} else if (btf_type_is_void(ptr_type)) {
+			/* kfunc returning 'void *' is equivalent to returning scalar */
+			mark_reg_unknown(env, regs, BPF_REG_0);
 		} else if (!__btf_type_is_struct(ptr_type)) {
 			if (!meta.r0_size) {
 				__u32 sz;