bpf: Allow struct argument in trampoline based programs
authorYonghong Song <yhs@fb.com>
Wed, 31 Aug 2022 15:26:46 +0000 (08:26 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 7 Sep 2022 02:51:14 +0000 (19:51 -0700)
Allow struct argument in trampoline based programs where
the struct size should be <= 16 bytes. In such cases, the argument
will be put into up to 2 registers for bpf, x86_64 and arm64
architectures.

To support arch-specific trampoline manipulation,
add arg_flags for additional struct information about arguments
in btf_func_model. Such information will be used in arch specific
function arch_prepare_bpf_trampoline() to prepare argument access
properly in trampoline.

Signed-off-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/r/20220831152646.2078089-1-yhs@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf.h
kernel/bpf/btf.c

index 9c16749..4d32f12 100644 (file)
@@ -727,10 +727,14 @@ enum bpf_cgroup_storage_type {
  */
 #define MAX_BPF_FUNC_REG_ARGS 5
 
+/* The argument is a structure. */
+#define BTF_FMODEL_STRUCT_ARG          BIT(0)
+
 struct btf_func_model {
        u8 ret_size;
        u8 nr_args;
        u8 arg_size[MAX_BPF_FUNC_ARGS];
+       u8 arg_flags[MAX_BPF_FUNC_ARGS];
 };
 
 /* Restore arguments before returning from trampoline to let original function
index 903719b..ea94527 100644 (file)
@@ -5328,6 +5328,34 @@ static bool is_int_ptr(struct btf *btf, const struct btf_type *t)
        return btf_type_is_int(t);
 }
 
+static u32 get_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto,
+                          int off)
+{
+       const struct btf_param *args;
+       const struct btf_type *t;
+       u32 offset = 0, nr_args;
+       int i;
+
+       if (!func_proto)
+               return off / 8;
+
+       nr_args = btf_type_vlen(func_proto);
+       args = (const struct btf_param *)(func_proto + 1);
+       for (i = 0; i < nr_args; i++) {
+               t = btf_type_skip_modifiers(btf, args[i].type, NULL);
+               offset += btf_type_is_ptr(t) ? 8 : roundup(t->size, 8);
+               if (off < offset)
+                       return i;
+       }
+
+       t = btf_type_skip_modifiers(btf, func_proto->type, NULL);
+       offset += btf_type_is_ptr(t) ? 8 : roundup(t->size, 8);
+       if (off < offset)
+               return nr_args;
+
+       return nr_args + 1;
+}
+
 bool btf_ctx_access(int off, int size, enum bpf_access_type type,
                    const struct bpf_prog *prog,
                    struct bpf_insn_access_aux *info)
@@ -5347,7 +5375,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
                        tname, off);
                return false;
        }
-       arg = off / 8;
+       arg = get_ctx_arg_idx(btf, t, off);
        args = (const struct btf_param *)(t + 1);
        /* if (t == NULL) Fall back to default BPF prog with
         * MAX_BPF_FUNC_REG_ARGS u64 arguments.
@@ -5417,7 +5445,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
        /* skip modifiers */
        while (btf_type_is_modifier(t))
                t = btf_type_by_id(btf, t->type);
-       if (btf_type_is_small_int(t) || btf_is_any_enum(t))
+       if (btf_type_is_small_int(t) || btf_is_any_enum(t) || __btf_type_is_struct(t))
                /* accessing a scalar */
                return true;
        if (!btf_type_is_ptr(t)) {
@@ -5881,7 +5909,7 @@ static int __get_type_size(struct btf *btf, u32 btf_id,
        if (btf_type_is_ptr(t))
                /* kernel size of pointer. Not BPF's size of pointer*/
                return sizeof(void *);
-       if (btf_type_is_int(t) || btf_is_any_enum(t))
+       if (btf_type_is_int(t) || btf_is_any_enum(t) || __btf_type_is_struct(t))
                return t->size;
        return -EINVAL;
 }
@@ -5901,8 +5929,10 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
                /* BTF function prototype doesn't match the verifier types.
                 * Fall back to MAX_BPF_FUNC_REG_ARGS u64 args.
                 */
-               for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++)
+               for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
                        m->arg_size[i] = 8;
+                       m->arg_flags[i] = 0;
+               }
                m->ret_size = 8;
                m->nr_args = MAX_BPF_FUNC_REG_ARGS;
                return 0;
@@ -5916,7 +5946,7 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
                return -EINVAL;
        }
        ret = __get_type_size(btf, func->type, &t);
-       if (ret < 0) {
+       if (ret < 0 || __btf_type_is_struct(t)) {
                bpf_log(log,
                        "The function %s return type %s is unsupported.\n",
                        tname, btf_kind_str[BTF_INFO_KIND(t->info)]);
@@ -5932,7 +5962,9 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
                        return -EINVAL;
                }
                ret = __get_type_size(btf, args[i].type, &t);
-               if (ret < 0) {
+
+               /* No support of struct argument size greater than 16 bytes */
+               if (ret < 0 || ret > 16) {
                        bpf_log(log,
                                "The function %s arg%d type %s is unsupported.\n",
                                tname, i, btf_kind_str[BTF_INFO_KIND(t->info)]);
@@ -5945,6 +5977,7 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
                        return -EINVAL;
                }
                m->arg_size[i] = ret;
+               m->arg_flags[i] = __btf_type_is_struct(t) ? BTF_FMODEL_STRUCT_ARG : 0;
        }
        m->nr_args = nargs;
        return 0;