netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Greg KH <gregkh@linuxfoundation.org>,
	Jiri Kosina <jikos@kernel.org>,
	Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	Martin KaFai Lau <kafai@fb.com>, Song Liu <songliubraving@fb.com>,
	Yonghong Song <yhs@fb.com>,
	John Fastabend <john.fastabend@gmail.com>,
	KP Singh <kpsingh@kernel.org>, Shuah Khan <shuah@kernel.org>,
	Dave Marchevsky <davemarchevsky@fb.com>,
	Joe Stringer <joe@cilium.io>, Jonathan Corbet <corbet@lwn.net>,
	Tero Kristo <tero.kristo@linux.intel.com>,
	lkml <linux-kernel@vger.kernel.org>,
	"open list:HID CORE LAYER" <linux-input@vger.kernel.org>,
	Networking <netdev@vger.kernel.org>, bpf <bpf@vger.kernel.org>,
	"open list:KERNEL SELFTEST FRAMEWORK" 
	<linux-kselftest@vger.kernel.org>,
	Linux Doc Mailing List <linux-doc@vger.kernel.org>
Subject: Re: [PATCH bpf-next v5 02/17] bpf/verifier: allow kfunc to return an allocated mem
Date: Thu, 19 May 2022 18:10:03 +0530	[thread overview]
Message-ID: <20220519124003.sixkr7u7rd6otgit@apollo.legion> (raw)
In-Reply-To: <CAO-hwJ+k7NjTieT6Uj1NvwGC7mxKw++U6PY5JqVQ=0=BsHVaoA@mail.gmail.com>

On Thu, May 19, 2022 at 05:35:56PM IST, Benjamin Tissoires wrote:
> Hi,
>
> thanks a lot for the quick review of these patches.
>
> On Wed, May 18, 2022 at 11:59 PM Kumar Kartikeya Dwivedi
> <memxor@gmail.com> wrote:
> >
> > On Thu, May 19, 2022 at 02:29:09AM IST, Benjamin Tissoires wrote:
> > > When a kfunc is not returning a pointer to a struct but to a plain type,
> > > we can consider it is a valid allocated memory assuming that:
> > > - one of the arguments is called rdonly_buf_size
> > > - or one of the arguments is called rdwr_buf_size
> > > - and this argument is a const from the caller point of view
> > >
> > > We can then use this parameter as the size of the allocated memory.
> > >
> > > The memory is either read-only or read-write based on the name
> > > of the size parameter.
> > >
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > >
> > > ---
> > >
> > > changes in v5:
> > > - updated PTR_TO_MEM comment in btf.c to match upstream
> > > - make it read-only or read-write based on the name of size
> > >
> > > new in v4
> > > ---
> > >  include/linux/btf.h   |  7 +++++
> > >  kernel/bpf/btf.c      | 41 +++++++++++++++++++++++-
> > >  kernel/bpf/verifier.c | 72 +++++++++++++++++++++++++++++++++----------
> > >  3 files changed, 102 insertions(+), 18 deletions(-)
> > >
> > > diff --git a/include/linux/btf.h b/include/linux/btf.h
> > > index 2611cea2c2b6..2a4feafc083e 100644
> > > --- a/include/linux/btf.h
> > > +++ b/include/linux/btf.h
> > > @@ -343,6 +343,13 @@ static inline struct btf_param *btf_params(const struct btf_type *t)
> > >       return (struct btf_param *)(t + 1);
> > >  }
> > >
> > > +struct bpf_reg_state;
> > > +
> > > +bool btf_is_kfunc_arg_mem_size(const struct btf *btf,
> > > +                            const struct btf_param *arg,
> > > +                            const struct bpf_reg_state *reg,
> > > +                            const char *name);
> > > +
> > >  #ifdef CONFIG_BPF_SYSCALL
> > >  struct bpf_prog;
> > >
> > > diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> > > index 7bccaa4646e5..2d11d178807c 100644
> > > --- a/kernel/bpf/btf.c
> > > +++ b/kernel/bpf/btf.c
> > > @@ -6049,6 +6049,31 @@ static bool is_kfunc_arg_mem_size(const struct btf *btf,
> > >       return true;
> > >  }
> > >
> > > +bool btf_is_kfunc_arg_mem_size(const struct btf *btf,
> > > +                            const struct btf_param *arg,
> > > +                            const struct bpf_reg_state *reg,
> > > +                            const char *name)
> > > +{
> > > +     int len, target_len = strlen(name);
> > > +     const struct btf_type *t;
> > > +     const char *param_name;
> > > +
> > > +     t = btf_type_skip_modifiers(btf, arg->type, NULL);
> > > +     if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
> > > +             return false;
> > > +
> > > +     param_name = btf_name_by_offset(btf, arg->name_off);
> > > +     if (str_is_empty(param_name))
> > > +             return false;
> > > +     len = strlen(param_name);
> > > +     if (len != target_len)
> > > +             return false;
> > > +     if (strncmp(param_name, name, target_len))
> > > +             return false;
> > > +
> > > +     return true;
> > > +}
> >
> > I think you don't need these checks. btf_check_kfunc_arg_match would have
> > already made sure scalar arguments receive scalar. The rest is just matching on
> > the argument name, which you can directly strcmp when setting up R0's type.
>
> OK.
>
> >
> > > +
> > >  static int btf_check_func_arg_match(struct bpf_verifier_env *env,
> > >                                   const struct btf *btf, u32 func_id,
> > >                                   struct bpf_reg_state *regs,
> > > @@ -6198,7 +6223,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
> > >                       if (reg->type == PTR_TO_BTF_ID) {
> > >                               reg_btf = reg->btf;
> > >                               reg_ref_id = reg->btf_id;
> > > -                             /* Ensure only one argument is referenced PTR_TO_BTF_ID */
> > > +                             /* Ensure only one argument is reference PTR_TO_BTF_ID or PTR_TO_MEM */
> >
> > But this part of the code would never be reached for PTR_TO_MEM, so the comment
> > would be false?
>
> Right, I mostly duplicated the code and the comment, so I'll drop it, thanks.
>
> >
> > >                               if (reg->ref_obj_id) {
> > >                                       if (ref_obj_id) {
> > >                                               bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
> > > @@ -6258,6 +6283,20 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
> > >                                       i++;
> > >                                       continue;
> > >                               }
> > > +
> > > +                             if (rel && reg->ref_obj_id) {
> > > +                                     /* Ensure only one argument is referenced PTR_TO_BTF_ID or PTR_TO_MEM */
> > > +                                     if (ref_obj_id) {
> > > +                                             bpf_log(log,
> > > +                                                     "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
> > > +                                                     regno,
> > > +                                                     reg->ref_obj_id,
> > > +                                                     ref_obj_id);
> > > +                                             return -EFAULT;
> > > +                                     }
> > > +                                     ref_regno = regno;
> > > +                                     ref_obj_id = reg->ref_obj_id;
> > > +                             }
> >
> > Why do we need this part? I don't see any code passing that __u8 * back into a
> > release function. The only release function I see that you are adding is
> > releasing a struct, which should be PTR_TO_BTF_ID and already supported.
>
> In my mind, we should have been able to acquire/release PTR_TO_MEM in
> the same way we are doing with PTR_TO_BTF_ID. But after fully writing
> down the code, it was not required, so maybe we can keep
> acquire/release only for PTR_TO_BTF_ID.
>

PTR_TO_MEM cannot easily work with acquire/release logic. There is no type of
the memory, so it is hard to know which release function it should be associated
with. Until that type information is added to the register state, it will happen
to work with all release functions that take any PTR_TO_MEM, which would break
things.

> >
> > Also acquire function should not return non-struct pointer. Can you also update
> > the if (acq && !btf_type_is_ptr(t)) check in check_kfunc_call to instead check
> > for btf_type_is_struct? The verbose log would be misleading now, but it was
> > based on the assumption only PTR_TO_BTF_ID as return pointer is supported.
>
> OK.
>
> >
> > >                       }
> > >
> > >                       resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index 9b59581026f8..084319073064 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -7219,13 +7219,14 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > >                           int *insn_idx_p)
> > >  {
> > >       const struct btf_type *t, *func, *func_proto, *ptr_type;
> > > -     struct bpf_reg_state *regs = cur_regs(env);
> > > +     struct bpf_reg_state *reg, *regs = cur_regs(env);
> > >       const char *func_name, *ptr_type_name;
> > > -     u32 i, nargs, func_id, ptr_type_id;
> > > +     u32 i, nargs, func_id, ptr_type_id, regno;
> > >       int err, insn_idx = *insn_idx_p;
> > >       const struct btf_param *args;
> > >       struct btf *desc_btf;
> > >       bool acq;
> > > +     size_t reg_rw_size = 0, reg_ro_size = 0;
> >
> > Not reverse X-mas tree.
>
> Oh, I didn't realize this was the applied convention. I'll amend
> (though the code refactoring from your comment below will probably
> change that hunk above).
>
> >
> > >
> > >       /* skip for now, but return error when we find this in fixup_kfunc_call */
> > >       if (!insn->imm)
> > > @@ -7266,8 +7267,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > >               }
> > >       }
> > >
> > > -     for (i = 0; i < CALLER_SAVED_REGS; i++)
> > > -             mark_reg_not_init(env, regs, caller_saved[i]);
> > > +     /* reset REG_0 */
> > > +     mark_reg_not_init(env, regs, BPF_REG_0);
> > >
> > >       /* Check return type */
> > >       t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL);
> > > @@ -7277,6 +7278,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > >               return -EINVAL;
> > >       }
> > >
> > > +     nargs = btf_type_vlen(func_proto);
> > > +     args = btf_params(func_proto);
> > > +
> > >       if (btf_type_is_scalar(t)) {
> > >               mark_reg_unknown(env, regs, BPF_REG_0);
> > >               mark_btf_func_reg_size(env, BPF_REG_0, t->size);
> > > @@ -7284,24 +7288,57 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > >               ptr_type = btf_type_skip_modifiers(desc_btf, t->type,
> > >                                                  &ptr_type_id);
> > >               if (!btf_type_is_struct(ptr_type)) {
> > > -                     ptr_type_name = btf_name_by_offset(desc_btf,
> > > -                                                        ptr_type->name_off);
> > > -                     verbose(env, "kernel function %s returns pointer type %s %s is not supported\n",
> > > -                             func_name, btf_type_str(ptr_type),
> > > -                             ptr_type_name);
> > > -                     return -EINVAL;
> > > +                     /* if we have an array, look for the arguments */
> > > +                     for (i = 0; i < nargs; i++) {
> > > +                             regno = i + BPF_REG_1;
> > > +                             reg = &regs[regno];
> > > +
> > > +                             /* look for any const scalar parameter of name "rdonly_buf_size"
> > > +                              * or "rdwr_buf_size"
> > > +                              */
> > > +                             if (!check_reg_arg(env, regno, SRC_OP) &&
> > > +                                 tnum_is_const(regs[regno].var_off)) {
> >
> > Instead of this, we should probably just check the argument that has its name as
> > rdonly/rdwr_buf_size inside btf_check_kfunc_arg_match and ensure there is only
> > one of those. No need for check_reg_arg, and just this tnum_is_const can also be
> > enforced inside btf_check_kfunc_arg_match. You can pass a struct like so:
> >
> >         struct bpf_kfunc_arg_meta {
> >                 u64 r0_size;
> >                 bool r0_rdonly;
> >         };
> >
> > and set its value to reg->var_off.value from inside the function in the argument
> > checking loop. Then you don't have to change the mark_reg_not_init order here.
> > All your code can be inside the if (btf_type_is_scalar(t)) branch.
>
> OK. I think I get it. Not sure I'll be able to get to it by the end of
> the week or next week, but I'll work on that cleanup for sure.
>
> >
> > Also, it would be nice to use this struct to signal the register that is being
> > released. Right now it's done using a > 0 return value (the if (err)) which is a
> > bit ugly. But up to you if you want to do that tiny cleanup.
>
> Should be easy enough to do, yes.
>
> >
> > > +                                     if (btf_is_kfunc_arg_mem_size(desc_btf, &args[i], reg,
> > > +                                                                   "rdonly_buf_size"))
> > > +                                             reg_ro_size = regs[regno].var_off.value;
> > > +                                     else if (btf_is_kfunc_arg_mem_size(desc_btf, &args[i], reg,
> > > +                                                                        "rdwr_buf_size"))
> > > +                                             reg_rw_size = regs[regno].var_off.value;
> > > +                             }
> > > +                     }
> > > +
> > > +                     if (!reg_rw_size && !reg_ro_size) {
> > > +                             ptr_type_name = btf_name_by_offset(desc_btf,
> > > +                                                                ptr_type->name_off);
> > > +                             verbose(env,
> > > +                                     "kernel function %s returns pointer type %s %s is not supported\n",
> > > +                                     func_name,
> > > +                                     btf_type_str(ptr_type),
> > > +                                     ptr_type_name);
> > > +                             return -EINVAL;
> > > +                     }
> > > +
> > > +                     mark_reg_known_zero(env, regs, BPF_REG_0);
> > > +                     regs[BPF_REG_0].type = PTR_TO_MEM;
> > > +                     regs[BPF_REG_0].mem_size = reg_ro_size + reg_rw_size;
> > > +
> > > +                     if (reg_ro_size)
> > > +                             regs[BPF_REG_0].type |= MEM_RDONLY;
> > > +             } else {
> > > +                     mark_reg_known_zero(env, regs, BPF_REG_0);
> > > +                     regs[BPF_REG_0].type = PTR_TO_BTF_ID;
> > > +                     regs[BPF_REG_0].btf = desc_btf;
> > > +                     regs[BPF_REG_0].btf_id = ptr_type_id;
> > > +                     mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *));
> > >               }
> > > -             mark_reg_known_zero(env, regs, BPF_REG_0);
> > > -             regs[BPF_REG_0].btf = desc_btf;
> > > -             regs[BPF_REG_0].type = PTR_TO_BTF_ID;
> > > -             regs[BPF_REG_0].btf_id = ptr_type_id;
> > > +
> > >               if (btf_kfunc_id_set_contains(desc_btf, resolve_prog_type(env->prog),
> > >                                             BTF_KFUNC_TYPE_RET_NULL, func_id)) {
> > >                       regs[BPF_REG_0].type |= PTR_MAYBE_NULL;
> > >                       /* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */
> > >                       regs[BPF_REG_0].id = ++env->id_gen;
> > >               }
> > > -             mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *));
> > > +
> >
> > Any reason to do this call only for PTR_TO_BTF_ID and not for PTR_TO_MEM?
>
> I must confess I am doing part of the things blindly, and it kind of
> worked, passed the tests and I was fine. So no, no reasons except that
> maybe at some point it broke what I was trying to do. I'll try to
> re-evaluate this line in the next version.
>

I think you can just leave that call as is, it should be made for both.

> Cheers,
> Benjamin
>
> >
> > >               if (acq) {
> > >                       int id = acquire_reference_state(env, insn_idx);
> > >
> > > @@ -7312,8 +7349,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > >               }
> > >       } /* else { add_kfunc_call() ensures it is btf_type_is_void(t) } */
> > >
> > > -     nargs = btf_type_vlen(func_proto);
> > > -     args = (const struct btf_param *)(func_proto + 1);
> > > +     for (i = 1 ; i < CALLER_SAVED_REGS; i++)
> > > +             mark_reg_not_init(env, regs, caller_saved[i]);
> > > +
> > >       for (i = 0; i < nargs; i++) {
> > >               u32 regno = i + 1;
> > >
> > > --
> > > 2.36.1
> > >
> >
> > --
> > Kartikeya
> >
>

--
Kartikeya

  reply	other threads:[~2022-05-19 12:39 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-18 20:59 [PATCH bpf-next v5 00/17] Introduce eBPF support for HID devices Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 01/17] bpf/btf: also allow kfunc in tracing and syscall programs Benjamin Tissoires
2022-05-21  2:34   ` Alexei Starovoitov
2022-05-18 20:59 ` [PATCH bpf-next v5 02/17] bpf/verifier: allow kfunc to return an allocated mem Benjamin Tissoires
2022-05-18 21:59   ` Kumar Kartikeya Dwivedi
2022-05-19 12:05     ` Benjamin Tissoires
2022-05-19 12:40       ` Kumar Kartikeya Dwivedi [this message]
2022-05-18 20:59 ` [PATCH bpf-next v5 03/17] bpf: prepare for more bpf syscall to be used from kernel and user space Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 04/17] libbpf: add map_get_fd_by_id and map_delete_elem in light skeleton Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 05/17] HID: core: store the unique system identifier in hid_device Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 06/17] HID: export hid_report_type to uapi Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 07/17] HID: initial BPF implementation Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 08/17] selftests/bpf: add tests for the HID-bpf initial implementation Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 09/17] HID: bpf: allocate data memory for device_event BPF programs Benjamin Tissoires
2022-05-18 23:13   ` kernel test robot
2022-05-18 20:59 ` [PATCH bpf-next v5 10/17] selftests/bpf/hid: add test to change the report size Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 11/17] HID: bpf: introduce hid_hw_request() Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 12/17] selftests/bpf: add tests for bpf_hid_hw_request Benjamin Tissoires
2022-05-18 22:20   ` Kumar Kartikeya Dwivedi
2022-05-19 12:12     ` Benjamin Tissoires
2022-05-19 12:51       ` Kumar Kartikeya Dwivedi
2022-05-19 13:13         ` Benjamin Tissoires
2022-05-19 13:44           ` Kumar Kartikeya Dwivedi
2022-05-19 15:47             ` Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 13/17] HID: bpf: allow to change the report descriptor Benjamin Tissoires
2022-05-21  2:46   ` Alexei Starovoitov
2022-05-18 20:59 ` [PATCH bpf-next v5 14/17] selftests/bpf: add report descriptor fixup tests Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 15/17] samples/bpf: add new hid_mouse example Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 16/17] selftests/bpf: Add a test for BPF_F_INSERT_HEAD Benjamin Tissoires
2022-05-18 20:59 ` [PATCH bpf-next v5 17/17] Documentation: add HID-BPF docs Benjamin Tissoires
2022-05-19  8:10 ` [PATCH bpf-next v5 00/17] Introduce eBPF support for HID devices Christoph Hellwig
2022-05-19  8:20   ` Greg KH
2022-05-19  8:38     ` Christoph Hellwig
2022-05-19 10:20       ` Benjamin Tissoires
2022-05-19 10:43         ` Toke Høiland-Jørgensen
2022-05-19 11:56           ` Benjamin Tissoires
2022-05-21  0:18             ` Alexei Starovoitov
2022-05-19 10:32       ` Greg KH
2022-05-19 11:46         ` Benjamin Tissoires
2022-05-21  2:40 ` patchwork-bot+netdevbpf
2022-05-27  7:26 ` Tero Kristo
2022-05-30 15:49   ` Benjamin Tissoires

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220519124003.sixkr7u7rd6otgit@apollo.legion \
    --to=memxor@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=benjamin.tissoires@redhat.com \
    --cc=bpf@vger.kernel.org \
    --cc=corbet@lwn.net \
    --cc=daniel@iogearbox.net \
    --cc=davemarchevsky@fb.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jikos@kernel.org \
    --cc=joe@cilium.io \
    --cc=john.fastabend@gmail.com \
    --cc=kafai@fb.com \
    --cc=kpsingh@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=shuah@kernel.org \
    --cc=songliubraving@fb.com \
    --cc=tero.kristo@linux.intel.com \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).