Re: [PATCH bpf-next 05/11] bpf: allow specifying a set of BTF IDs for helper arguments

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Sep 4, 2020 at 4:30 AM Lorenz Bauer <lmb@xxxxxxxxxxxxxx> wrote:
>
> Function prototypes using ARG_PTR_TO_BTF_ID currently use two ways to signal
> which BTF IDs are acceptable. First, bpf_func_proto.btf_id is an array of
> IDs, one for each argument. This array is only accessed up to the highest
> numbered argument that uses ARG_PTR_TO_BTF_ID and may therefore be less than
> five arguments long. It usually points at a BTF_ID_LIST. Second, check_btf_id
> is a function pointer that is called by the verifier if present. It gets the
> actual BTF ID of the register, and the argument number we're currently checking.
> It turns out that the only user check_arg_btf_id ignores the argument, and is
> simply used to check whether the BTF ID matches one of the socket types.
>
> Replace both of these mechanisms with explicit btf_id_sets for each argument
> in a function proto. The verifier can now check that a PTR_TO_BTF_ID is one
> of several IDs, and the code that does the type checking becomes simpler.
>
> Add a small optimisation to btf_set_contains for the common case of a set with
> a single entry.
>
> Signed-off-by: Lorenz Bauer <lmb@xxxxxxxxxxxxxx>
> ---

You are replacing a more generic and powerful capability with a more
restricted one because no one is yet using a generic one fully. It
might be ok and we'll never need a more generic way to check BTF IDs.
But it will be funny if we will be adding this back soon because a
static set of BTF IDs don't cut it for some cases :)

I don't mind this change, but I wonder what others think about this.

>  include/linux/bpf.h            | 22 ++++++++++---------
>  kernel/bpf/bpf_inode_storage.c |  8 +++----
>  kernel/bpf/btf.c               | 22 ++++++-------------
>  kernel/bpf/stackmap.c          |  5 +++--
>  kernel/bpf/verifier.c          | 39 +++++++++++++---------------------
>  kernel/trace/bpf_trace.c       | 15 +++++++------
>  net/core/bpf_sk_storage.c      | 10 +++++----
>  net/core/filter.c              | 31 ++++++++++-----------------
>  net/ipv4/bpf_tcp_ca.c          | 24 +++++++++------------
>  9 files changed, 76 insertions(+), 100 deletions(-)
>

[...]

> diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c
> index 75be02799c0f..d447d2655cce 100644
> --- a/kernel/bpf/bpf_inode_storage.c
> +++ b/kernel/bpf/bpf_inode_storage.c
> @@ -249,9 +249,9 @@ const struct bpf_map_ops inode_storage_map_ops = {
>         .map_owner_storage_ptr = inode_storage_ptr,
>  };
>
> -BTF_ID_LIST(bpf_inode_storage_btf_ids)
> -BTF_ID_UNUSED
> +BTF_SET_START(bpf_inode_storage_btf_ids)
>  BTF_ID(struct, inode)
> +BTF_SET_END(bpf_inode_storage_btf_ids)

with your change single-element BTF ID set becomes a very common case,
so having a simple macro that combines BTF_SET_START + BTF_ID +
BTF_SET_END in one simple macro would be useful, I think

>
>  const struct bpf_func_proto bpf_inode_storage_get_proto = {
>         .func           = bpf_inode_storage_get,
> @@ -259,9 +259,9 @@ const struct bpf_func_proto bpf_inode_storage_get_proto = {
>         .ret_type       = RET_PTR_TO_MAP_VALUE_OR_NULL,
>         .arg1_type      = ARG_CONST_MAP_PTR,
>         .arg2_type      = ARG_PTR_TO_BTF_ID,
> +       .arg2_btf_ids   = &bpf_inode_storage_btf_ids,
>         .arg3_type      = ARG_PTR_TO_MAP_VALUE_OR_NULL,
>         .arg4_type      = ARG_ANYTHING,
> -       .btf_id         = bpf_inode_storage_btf_ids,
>  };
>

[...]

> @@ -4065,26 +4066,21 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
>         }
>
>         if (type == PTR_TO_BTF_ID) {
> -               bool ids_match = false;
> +               if (fn->arg_btf_ids[arg])
> +                       btf_ids = fn->arg_btf_ids[arg];

nit: no need for the if part, just assign directly, even if its NULL

>
> -               if (!fn->check_btf_id) {
> -                       if (reg->btf_id != meta->btf_id) {
> -                               ids_match = btf_struct_ids_match(&env->log, reg->off, reg->btf_id,
> -                                                                meta->btf_id);
> -                               if (!ids_match) {
> -                                       verbose(env, "Helper has type %s got %s in R%d\n",
> -                                               kernel_type_name(meta->btf_id),
> -                                               kernel_type_name(reg->btf_id), regno);
> -                                       return -EACCES;
> -                               }
> -                       }
> -               } else if (!fn->check_btf_id(reg->btf_id, arg)) {
> -                       verbose(env, "Helper does not support %s in R%d\n",
> -                               kernel_type_name(reg->btf_id), regno);
> +               if (!btf_ids) {
> +                       verbose(env, "verifier internal error: missing BTF IDs\n");
> +                       return -EFAULT;
> +               }
>
> +               if (!btf_struct_ids_match(&env->log, reg->off, reg->btf_id,
> +                                         btf_ids)) {
> +                       verbose(env, "R%d has incompatible type %s\n", regno,
> +                               kernel_type_name(reg->btf_id));
>                         return -EACCES;
>                 }
> -               if ((reg->off && !ids_match) || !tnum_is_const(reg->var_off) || reg->var_off.value) {
> +               if (!tnum_is_const(reg->var_off) || reg->var_off.value) {
>                         verbose(env, "R%d is a pointer to in-kernel struct with non-zero offset\n",
>                                 regno);
>                         return -EACCES;

[...]



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux