]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
bpf: verifier: Relax caller requirements for kfunc projection type args
authorDaniel Xu <dxu@dxuuu.xyz>
Wed, 12 Jun 2024 15:58:32 +0000 (09:58 -0600)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 12 Jun 2024 18:01:31 +0000 (11:01 -0700)
Currently, if a kfunc accepts a projection type as an argument (eg
struct __sk_buff *), the caller must exactly provide exactly the same
type with provable provenance.

However in practice, kfuncs that accept projection types _must_ cast to
the underlying type before use b/c projection type layouts are
completely made up. Thus, it is ok to relax the verifier rules around
implicit conversions.

We will use this functionality in the next commit when we align kfuncs
to user-facing types.

Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
Link: https://lore.kernel.org/r/e2c025cb09ccfd4af1ec9e18284dc3cecff7514d.1718207789.git.dxu@dxuuu.xyz
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/btf.h
kernel/bpf/btf.c
kernel/bpf/verifier.c

index f9e56fd12a9fd52d94347da85f7ae5b37f5ef330..56d91daacdba303f4a6d891660563d2b1a6e28d8 100644 (file)
@@ -531,6 +531,7 @@ s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
 int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
                                struct module *owner);
 struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id);
+bool btf_is_projection_of(const char *pname, const char *tname);
 bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
                           const struct btf_type *t, enum bpf_prog_type prog_type,
                           int arg);
index 7928d920056f737fc61b73d4a49d4e93af431738..ce47079682176195f5ae62e8bb4748d92bae5189 100644 (file)
@@ -5820,6 +5820,15 @@ static int find_kern_ctx_type_id(enum bpf_prog_type prog_type)
        return ctx_type->type;
 }
 
+bool btf_is_projection_of(const char *pname, const char *tname)
+{
+       if (strcmp(pname, "__sk_buff") == 0 && strcmp(tname, "sk_buff") == 0)
+               return true;
+       if (strcmp(pname, "xdp_md") == 0 && strcmp(tname, "xdp_buff") == 0)
+               return true;
+       return false;
+}
+
 bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
                          const struct btf_type *t, enum bpf_prog_type prog_type,
                          int arg)
@@ -5882,9 +5891,7 @@ again:
         * int socket_filter_bpf_prog(struct __sk_buff *skb)
         * { // no fields of skb are ever used }
         */
-       if (strcmp(ctx_tname, "__sk_buff") == 0 && strcmp(tname, "sk_buff") == 0)
-               return true;
-       if (strcmp(ctx_tname, "xdp_md") == 0 && strcmp(tname, "xdp_buff") == 0)
+       if (btf_is_projection_of(ctx_tname, tname))
                return true;
        if (strcmp(ctx_tname, tname)) {
                /* bpf_user_pt_regs_t is a typedef, so resolve it to
index 20ac9cfd54dd74ac2e9ff95ae1a7a461caa68eaf..dcac6119d810212e1956df321aff330c4c5eb03f 100644 (file)
@@ -11265,6 +11265,8 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
        bool strict_type_match = false;
        const struct btf *reg_btf;
        const char *reg_ref_tname;
+       bool taking_projection;
+       bool struct_same;
        u32 reg_ref_id;
 
        if (base_type(reg->type) == PTR_TO_BTF_ID) {
@@ -11308,7 +11310,13 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env,
 
        reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, &reg_ref_id);
        reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off);
-       if (!btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->off, meta->btf, ref_id, strict_type_match)) {
+       struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, reg->off, meta->btf, ref_id, strict_type_match);
+       /* If kfunc is accepting a projection type (ie. __sk_buff), it cannot
+        * actually use it -- it must cast to the underlying type. So we allow
+        * caller to pass in the underlying type.
+        */
+       taking_projection = btf_is_projection_of(ref_tname, reg_ref_tname);
+       if (!taking_projection && !struct_same) {
                verbose(env, "kernel function %s args#%d expected pointer to %s %s but R%d has a pointer to %s %s\n",
                        meta->func_name, argno, btf_type_str(ref_t), ref_tname, argno + 1,
                        btf_type_str(reg_ref_t), reg_ref_tname);