All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lorenz Bauer <lmb@cloudflare.com>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>, bpf <bpf@vger.kernel.org>,
	Networking <netdev@vger.kernel.org>,
	Martin KaFai Lau <kafai@fb.com>
Subject: Re: [PATCH bpf-next v4 11/11] bpf: use a table to drive helper arg type checks
Date: Wed, 23 Sep 2020 10:45:47 +0100	[thread overview]
Message-ID: <CACAyw9-K15TMNzWkg3PtFt47X2iQCD9fwbUaVdF2jJZn3poZ3A@mail.gmail.com> (raw)
In-Reply-To: <20200922200748.gv6x6yhkyxnbqxx4@ast-mbp.dhcp.thefacebook.com>

On Tue, 22 Sep 2020 at 21:07, Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Tue, Sep 22, 2020 at 09:20:27AM +0100, Lorenz Bauer wrote:
> > On Mon, 21 Sep 2020 at 23:23, Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > >
> > > On Mon, Sep 21, 2020 at 01:12:27PM +0100, Lorenz Bauer wrote:
> > > > +struct bpf_reg_types {
> > > > +     const enum bpf_reg_type types[10];
> > > > +};
> > >
> > > any idea on how to make it more robust?
> >
> > I kind of copied this from the bpf_iter context. I prototyped using an
> > enum bpf_reg_type * and then terminating the array with NOT_INIT.
> > Writing this out is more involved, and might need some macro magic to
> > make it palatable. The current approach is a lot simpler, and I
> > figured that the compiler will error out if we ever exceed the 10
> > items.
>
> The compiler will be silent if number of types is exactly 10,
> but at run-time the loop will access out of bounds.

Which loop do you refer to?

The one in check_reg_type shouldn't go out of bounds due to ARRAY_SIZE:

    for (i = 0; i < ARRAY_SIZE(compatible->types); i++) {
        expected = compatible->types[i];
        if (expected == NOT_INIT)
            break;

>
> > >
> > > > +
> > > > +static const struct bpf_reg_types *compatible_reg_types[] = {
> > > > +     [ARG_PTR_TO_MAP_KEY]            = &map_key_value_types,
> > > > +     [ARG_PTR_TO_MAP_VALUE]          = &map_key_value_types,
> > > > +     [ARG_PTR_TO_UNINIT_MAP_VALUE]   = &map_key_value_types,
> > > > +     [ARG_PTR_TO_MAP_VALUE_OR_NULL]  = &map_key_value_types,
> > > > +     [ARG_CONST_SIZE]                = &scalar_types,
> > > > +     [ARG_CONST_SIZE_OR_ZERO]        = &scalar_types,
> > > > +     [ARG_CONST_ALLOC_SIZE_OR_ZERO]  = &scalar_types,
> > > > +     [ARG_CONST_MAP_PTR]             = &const_map_ptr_types,
> > > > +     [ARG_PTR_TO_CTX]                = &context_types,
> > > > +     [ARG_PTR_TO_CTX_OR_NULL]        = &context_types,
> > > > +     [ARG_PTR_TO_SOCK_COMMON]        = &sock_types,
> > > > +     [ARG_PTR_TO_SOCKET]             = &fullsock_types,
> > > > +     [ARG_PTR_TO_SOCKET_OR_NULL]     = &fullsock_types,
> > > > +     [ARG_PTR_TO_BTF_ID]             = &btf_ptr_types,
> > > > +     [ARG_PTR_TO_SPIN_LOCK]          = &spin_lock_types,
> > > > +     [ARG_PTR_TO_MEM]                = &mem_types,
> > > > +     [ARG_PTR_TO_MEM_OR_NULL]        = &mem_types,
> > > > +     [ARG_PTR_TO_UNINIT_MEM]         = &mem_types,
> > > > +     [ARG_PTR_TO_ALLOC_MEM]          = &alloc_mem_types,
> > > > +     [ARG_PTR_TO_ALLOC_MEM_OR_NULL]  = &alloc_mem_types,
> > > > +     [ARG_PTR_TO_INT]                = &int_ptr_types,
> > > > +     [ARG_PTR_TO_LONG]               = &int_ptr_types,
> > > > +     [__BPF_ARG_TYPE_MAX]            = NULL,
> > >
> > > I don't understand what this extra value is for.
> > > I tried:
> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > > index fc5c901c7542..87b0d5dcc1ff 100644
> > > --- a/include/linux/bpf.h
> > > +++ b/include/linux/bpf.h
> > > @@ -292,7 +292,6 @@ enum bpf_arg_type {
> > >         ARG_PTR_TO_ALLOC_MEM,   /* pointer to dynamically allocated memory */
> > >         ARG_PTR_TO_ALLOC_MEM_OR_NULL,   /* pointer to dynamically allocated memory or NULL */
> > >         ARG_CONST_ALLOC_SIZE_OR_ZERO,   /* number of allocated bytes requested */
> > > -       __BPF_ARG_TYPE_MAX,
> > >  };
> > >
> > >  /* type of values returned from helper functions */
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index 15ab889b0a3f..83faa67858b6 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -4025,7 +4025,6 @@ static const struct bpf_reg_types *compatible_reg_types[] = {
> > >         [ARG_PTR_TO_ALLOC_MEM_OR_NULL]  = &alloc_mem_types,
> > >         [ARG_PTR_TO_INT]                = &int_ptr_types,
> > >         [ARG_PTR_TO_LONG]               = &int_ptr_types,
> > > -       [__BPF_ARG_TYPE_MAX]            = NULL,
> > >  };
> > >
> > > and everything is fine as I think it should be.
> > >
> > > > +     compatible = compatible_reg_types[arg_type];
> > > > +     if (!compatible) {
> > > > +             verbose(env, "verifier internal error: unsupported arg type %d\n", arg_type);
> > > >               return -EFAULT;
> > > >       }
> > >
> > > This check will trigger the same way when somebody adds new ARG_* and doesn't add to the table.
> >
> > I think in that case that value of compatible will be undefined, since
> > it points past the end of compatible_reg_types. Hence the
> > __BPF_ARG_TYPE_MAX to ensure that the array has a NULL slot for new
> > arg types.
>
> I still don't see a point.
> If anyone adds one more ARG_ to the end (or anywhere else)
> the compatible_reg_types array will be zero inited in that place by the compiler.
> Just like it does already for ARG_ANYTHING and ARG_DONTCARE.

I looked up designated initializers when I wrote this, since I wasn't
super familiar with them:
https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html#Designated-Inits

    Note that the length of the array is the highest value specified plus one.

So ARG_ANYTHING and ARG_DONTCARE are OK since there is a higher enum
value present in the initializer. If someone adds a new item to enum
bpf_arg_type I assume they would add it to the end. In that case the
highest value of the initializer doesn't change, and then indexing
into compatible_reg_types with the new enum value would be out of
bounds. Adding __BPF_ARG_TYPE_MAX fixes that.

It's very possible I misunderstood how this whole contraption works,
happy to send a patch.

-- 
Lorenz Bauer  |  Systems Engineer
6th Floor, County Hall/The Riverside Building, SE1 7PB, UK

www.cloudflare.com

  reply	other threads:[~2020-09-23  9:46 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-21 12:12 [PATCH RESEND bpf-next v4 00/11] Make check_func_arg type checks table driven Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 01/11] btf: make btf_set_contains take a const pointer Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 02/11] bpf: check scalar or invalid register in check_helper_mem_access Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 03/11] btf: Add BTF_ID_LIST_SINGLE macro Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 04/11] bpf: allow specifying a BTF ID per argument in function protos Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 05/11] bpf: make BTF pointer type checking generic Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 06/11] bpf: make reference tracking generic Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 07/11] bpf: make context access check generic Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 08/11] bpf: set meta->raw_mode for pointers close to use Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 09/11] bpf: check ARG_PTR_TO_SPINLOCK register type in check_func_arg Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 10/11] bpf: hoist type checking for nullable arg types Lorenz Bauer
2020-09-21 12:12 ` [PATCH bpf-next v4 11/11] bpf: use a table to drive helper arg type checks Lorenz Bauer
2020-09-21 22:23   ` Alexei Starovoitov
2020-09-22  8:20     ` Lorenz Bauer
2020-09-22 20:07       ` Alexei Starovoitov
2020-09-23  9:45         ` Lorenz Bauer [this message]
2020-09-23 15:24           ` Alexei Starovoitov
  -- strict thread matches above, loose matches on Subject: below --
2020-09-16 17:52 [PATCH bpf-next v4 00/11] Make check_func_arg type checks table driven Lorenz Bauer
2020-09-16 17:52 ` [PATCH bpf-next v4 11/11] bpf: use a table to drive helper arg type checks Lorenz Bauer

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=CACAyw9-K15TMNzWkg3PtFt47X2iQCD9fwbUaVdF2jJZn3poZ3A@mail.gmail.com \
    --to=lmb@cloudflare.com \
    --cc=alexei.starovoitov@gmail.com \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kafai@fb.com \
    --cc=netdev@vger.kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.