]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf: move subprog call logic back to verifier.c
authorAndrii Nakryiko <andrii@kernel.org>
Fri, 15 Dec 2023 01:13:28 +0000 (17:13 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 20 Dec 2023 02:06:46 +0000 (18:06 -0800)
Subprog call logic in btf_check_subprog_call() currently has both a lot
of BTF parsing logic (which is, presumably, what justified putting it
into btf.c), but also a bunch of register state checks, some of each
utilize deep verifier logic helpers, necessarily exported from
verifier.c: check_ptr_off_reg(), check_func_arg_reg_off(),
and check_mem_reg().

Going forward, btf_check_subprog_call() will have a minimum of
BTF-related logic, but will get more internal verifier logic related to
register state manipulation. So move it into verifier.c to minimize
amount of verifier-specific logic exposed to btf.c.

We do this move before refactoring btf_check_func_arg_match() to
preserve as much history post-refactoring as possible.

No functional changes.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231215011334.2307144-5-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf.h
include/linux/bpf_verifier.h
kernel/bpf/btf.c
kernel/bpf/verifier.c

index d0d7eff22b8a9647c5cfc87a16eb119bdc0fe156..7671530d6e4e08a973e7b5ed02dee626fc3fcf31 100644 (file)
@@ -2466,8 +2466,6 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
                           struct btf_func_model *m);
 
 struct bpf_reg_state;
-int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
-                          struct bpf_reg_state *regs);
 int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog);
 int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *prog,
                         struct btf *btf, const struct btf_type *t);
index d3ea9ef0476777db6289a9239ec7cbf6e592c47e..d07d857ca67fe17676962fe24c128103174f7529 100644 (file)
@@ -785,14 +785,6 @@ bpf_prog_offload_replace_insn(struct bpf_verifier_env *env, u32 off,
 void
 bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt);
 
-int check_ptr_off_reg(struct bpf_verifier_env *env,
-                     const struct bpf_reg_state *reg, int regno);
-int check_func_arg_reg_off(struct bpf_verifier_env *env,
-                          const struct bpf_reg_state *reg, int regno,
-                          enum bpf_arg_type arg_type);
-int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
-                  u32 regno, u32 mem_size);
-
 /* this lives here instead of in bpf.h because it needs to dereference tgt_prog */
 static inline u64 bpf_trampoline_compute_key(const struct bpf_prog *tgt_prog,
                                             struct btf *btf, u32 btf_id)
index d321340e16f1a6366acd0173873a88a2441a4b66..341811bcca538df75aeb4f7c0b4a2808648bf04e 100644 (file)
@@ -6765,145 +6765,6 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr
        return btf_check_func_type_match(log, btf1, t1, btf2, t2);
 }
 
-static int btf_check_func_arg_match(struct bpf_verifier_env *env,
-                                   const struct btf *btf, u32 func_id,
-                                   struct bpf_reg_state *regs,
-                                   bool ptr_to_mem_ok)
-{
-       enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
-       struct bpf_verifier_log *log = &env->log;
-       const char *func_name, *ref_tname;
-       const struct btf_type *t, *ref_t;
-       const struct btf_param *args;
-       u32 i, nargs, ref_id;
-       int ret;
-
-       t = btf_type_by_id(btf, func_id);
-       if (!t || !btf_type_is_func(t)) {
-               /* These checks were already done by the verifier while loading
-                * struct bpf_func_info or in add_kfunc_call().
-                */
-               bpf_log(log, "BTF of func_id %u doesn't point to KIND_FUNC\n",
-                       func_id);
-               return -EFAULT;
-       }
-       func_name = btf_name_by_offset(btf, t->name_off);
-
-       t = btf_type_by_id(btf, t->type);
-       if (!t || !btf_type_is_func_proto(t)) {
-               bpf_log(log, "Invalid BTF of func %s\n", func_name);
-               return -EFAULT;
-       }
-       args = (const struct btf_param *)(t + 1);
-       nargs = btf_type_vlen(t);
-       if (nargs > MAX_BPF_FUNC_REG_ARGS) {
-               bpf_log(log, "Function %s has %d > %d args\n", func_name, nargs,
-                       MAX_BPF_FUNC_REG_ARGS);
-               return -EINVAL;
-       }
-
-       /* check that BTF function arguments match actual types that the
-        * verifier sees.
-        */
-       for (i = 0; i < nargs; i++) {
-               enum bpf_arg_type arg_type = ARG_DONTCARE;
-               u32 regno = i + 1;
-               struct bpf_reg_state *reg = &regs[regno];
-
-               t = btf_type_skip_modifiers(btf, args[i].type, NULL);
-               if (btf_type_is_scalar(t)) {
-                       if (reg->type == SCALAR_VALUE)
-                               continue;
-                       bpf_log(log, "R%d is not a scalar\n", regno);
-                       return -EINVAL;
-               }
-
-               if (!btf_type_is_ptr(t)) {
-                       bpf_log(log, "Unrecognized arg#%d type %s\n",
-                               i, btf_type_str(t));
-                       return -EINVAL;
-               }
-
-               ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
-               ref_tname = btf_name_by_offset(btf, ref_t->name_off);
-
-               ret = check_func_arg_reg_off(env, reg, regno, arg_type);
-               if (ret < 0)
-                       return ret;
-
-               if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
-                       /* If function expects ctx type in BTF check that caller
-                        * is passing PTR_TO_CTX.
-                        */
-                       if (reg->type != PTR_TO_CTX) {
-                               bpf_log(log,
-                                       "arg#%d expected pointer to ctx, but got %s\n",
-                                       i, btf_type_str(t));
-                               return -EINVAL;
-                       }
-               } else if (ptr_to_mem_ok) {
-                       const struct btf_type *resolve_ret;
-                       u32 type_size;
-
-                       resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
-                       if (IS_ERR(resolve_ret)) {
-                               bpf_log(log,
-                                       "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
-                                       i, btf_type_str(ref_t), ref_tname,
-                                       PTR_ERR(resolve_ret));
-                               return -EINVAL;
-                       }
-
-                       if (check_mem_reg(env, reg, regno, type_size))
-                               return -EINVAL;
-               } else {
-                       bpf_log(log, "reg type unsupported for arg#%d function %s#%d\n", i,
-                               func_name, func_id);
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-/* Compare BTF of a function call with given bpf_reg_state.
- * Returns:
- * EFAULT - there is a verifier bug. Abort verification.
- * EINVAL - there is a type mismatch or BTF is not available.
- * 0 - BTF matches with what bpf_reg_state expects.
- * Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
- */
-int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
-                          struct bpf_reg_state *regs)
-{
-       struct bpf_prog *prog = env->prog;
-       struct btf *btf = prog->aux->btf;
-       bool is_global;
-       u32 btf_id;
-       int err;
-
-       if (!prog->aux->func_info)
-               return -EINVAL;
-
-       btf_id = prog->aux->func_info[subprog].type_id;
-       if (!btf_id)
-               return -EFAULT;
-
-       if (prog->aux->func_info_aux[subprog].unreliable)
-               return -EINVAL;
-
-       is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
-       err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global);
-
-       /* Compiler optimizations can remove arguments from static functions
-        * or mismatched type can be passed into a global function.
-        * In such cases mark the function as unreliable from BTF point of view.
-        */
-       if (err)
-               prog->aux->func_info_aux[subprog].unreliable = true;
-       return err;
-}
-
 /* Process BTF of a function to produce high-level expectation of function
  * arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information
  * is cached in subprog info for reuse.
index 6c9ecb86a8d9788ba762244a80f237c8cd2d6bcf..f48e49f2d4828c918fe22a3214a003742b5974aa 100644 (file)
@@ -5127,8 +5127,8 @@ static int __check_ptr_off_reg(struct bpf_verifier_env *env,
        return 0;
 }
 
-int check_ptr_off_reg(struct bpf_verifier_env *env,
-                     const struct bpf_reg_state *reg, int regno)
+static int check_ptr_off_reg(struct bpf_verifier_env *env,
+                            const struct bpf_reg_state *reg, int regno)
 {
        return __check_ptr_off_reg(env, reg, regno, false);
 }
@@ -7300,8 +7300,8 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
        return err;
 }
 
-int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
-                  u32 regno, u32 mem_size)
+static int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+                        u32 regno, u32 mem_size)
 {
        bool may_be_null = type_may_be_null(reg->type);
        struct bpf_reg_state saved_reg;
@@ -8286,9 +8286,9 @@ reg_find_field_offset(const struct bpf_reg_state *reg, s32 off, u32 fields)
        return field;
 }
 
-int check_func_arg_reg_off(struct bpf_verifier_env *env,
-                          const struct bpf_reg_state *reg, int regno,
-                          enum bpf_arg_type arg_type)
+static int check_func_arg_reg_off(struct bpf_verifier_env *env,
+                                 const struct bpf_reg_state *reg, int regno,
+                                 enum bpf_arg_type arg_type)
 {
        u32 type = reg->type;
 
@@ -9249,6 +9249,145 @@ err_out:
        return err;
 }
 
+static int btf_check_func_arg_match(struct bpf_verifier_env *env,
+                                   const struct btf *btf, u32 func_id,
+                                   struct bpf_reg_state *regs,
+                                   bool ptr_to_mem_ok)
+{
+       enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
+       struct bpf_verifier_log *log = &env->log;
+       const char *func_name, *ref_tname;
+       const struct btf_type *t, *ref_t;
+       const struct btf_param *args;
+       u32 i, nargs, ref_id;
+       int ret;
+
+       t = btf_type_by_id(btf, func_id);
+       if (!t || !btf_type_is_func(t)) {
+               /* These checks were already done by the verifier while loading
+                * struct bpf_func_info or in add_kfunc_call().
+                */
+               bpf_log(log, "BTF of func_id %u doesn't point to KIND_FUNC\n",
+                       func_id);
+               return -EFAULT;
+       }
+       func_name = btf_name_by_offset(btf, t->name_off);
+
+       t = btf_type_by_id(btf, t->type);
+       if (!t || !btf_type_is_func_proto(t)) {
+               bpf_log(log, "Invalid BTF of func %s\n", func_name);
+               return -EFAULT;
+       }
+       args = (const struct btf_param *)(t + 1);
+       nargs = btf_type_vlen(t);
+       if (nargs > MAX_BPF_FUNC_REG_ARGS) {
+               bpf_log(log, "Function %s has %d > %d args\n", func_name, nargs,
+                       MAX_BPF_FUNC_REG_ARGS);
+               return -EINVAL;
+       }
+
+       /* check that BTF function arguments match actual types that the
+        * verifier sees.
+        */
+       for (i = 0; i < nargs; i++) {
+               enum bpf_arg_type arg_type = ARG_DONTCARE;
+               u32 regno = i + 1;
+               struct bpf_reg_state *reg = &regs[regno];
+
+               t = btf_type_skip_modifiers(btf, args[i].type, NULL);
+               if (btf_type_is_scalar(t)) {
+                       if (reg->type == SCALAR_VALUE)
+                               continue;
+                       bpf_log(log, "R%d is not a scalar\n", regno);
+                       return -EINVAL;
+               }
+
+               if (!btf_type_is_ptr(t)) {
+                       bpf_log(log, "Unrecognized arg#%d type %s\n",
+                               i, btf_type_str(t));
+                       return -EINVAL;
+               }
+
+               ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
+               ref_tname = btf_name_by_offset(btf, ref_t->name_off);
+
+               ret = check_func_arg_reg_off(env, reg, regno, arg_type);
+               if (ret < 0)
+                       return ret;
+
+               if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
+                       /* If function expects ctx type in BTF check that caller
+                        * is passing PTR_TO_CTX.
+                        */
+                       if (reg->type != PTR_TO_CTX) {
+                               bpf_log(log,
+                                       "arg#%d expected pointer to ctx, but got %s\n",
+                                       i, btf_type_str(t));
+                               return -EINVAL;
+                       }
+               } else if (ptr_to_mem_ok) {
+                       const struct btf_type *resolve_ret;
+                       u32 type_size;
+
+                       resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
+                       if (IS_ERR(resolve_ret)) {
+                               bpf_log(log,
+                                       "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
+                                       i, btf_type_str(ref_t), ref_tname,
+                                       PTR_ERR(resolve_ret));
+                               return -EINVAL;
+                       }
+
+                       if (check_mem_reg(env, reg, regno, type_size))
+                               return -EINVAL;
+               } else {
+                       bpf_log(log, "reg type unsupported for arg#%d function %s#%d\n", i,
+                               func_name, func_id);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/* Compare BTF of a function call with given bpf_reg_state.
+ * Returns:
+ * EFAULT - there is a verifier bug. Abort verification.
+ * EINVAL - there is a type mismatch or BTF is not available.
+ * 0 - BTF matches with what bpf_reg_state expects.
+ * Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
+ */
+static int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
+                                 struct bpf_reg_state *regs)
+{
+       struct bpf_prog *prog = env->prog;
+       struct btf *btf = prog->aux->btf;
+       bool is_global;
+       u32 btf_id;
+       int err;
+
+       if (!prog->aux->func_info)
+               return -EINVAL;
+
+       btf_id = prog->aux->func_info[subprog].type_id;
+       if (!btf_id)
+               return -EFAULT;
+
+       if (prog->aux->func_info_aux[subprog].unreliable)
+               return -EINVAL;
+
+       is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
+       err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global);
+
+       /* Compiler optimizations can remove arguments from static functions
+        * or mismatched type can be passed into a global function.
+        * In such cases mark the function as unreliable from BTF point of view.
+        */
+       if (err)
+               prog->aux->func_info_aux[subprog].unreliable = true;
+       return err;
+}
+
 static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
                              int insn_idx, int subprog,
                              set_callee_state_fn set_callee_state_cb)