]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
verifier: parse BTF type tags for function arguments
authorEmil Tsalapatis <emil@etsalapatis.com>
Tue, 2 Jun 2026 00:41:16 +0000 (20:41 -0400)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 2 Jun 2026 01:42:33 +0000 (18:42 -0700)
The BTF parsing logic for function arguments goes through
the arguments' decl tags, but does not go into their type
tags. Add type tag parsing for function arguments.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
Link: https://lore.kernel.org/r/20260602004120.17087-3-emil@etsalapatis.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/btf.c

index f429f6f58cb279433071d9846913020a672c1e9a..6fb3461e3ac2b3cdad54c5357b2664fbd3ea7d1c 100644 (file)
@@ -7802,6 +7802,84 @@ enum btf_arg_tag {
        ARG_TAG_ARENA     = BIT_ULL(5),
 };
 
+static int btf_scan_decl_tags(struct bpf_verifier_env *env,
+                             const struct btf *btf,
+                             const struct btf_type *fn_t,
+                             u32 arg_idx, bool is_global, u32 *tags)
+{
+       int id = btf_named_start_id(btf, false) - 1;
+
+       /*
+        * The 'arg:<tag>' decl_tag takes precedence over the derivation
+        * of the register type from the BTF type itself.
+        */
+       while ((id = btf_find_next_decl_tag(btf, fn_t, arg_idx, "arg:", id)) > 0) {
+               const struct btf_type *tag_t = btf_type_by_id(btf, id);
+               const char *tag = __btf_name_by_offset(btf, tag_t->name_off) + 4;
+
+               /* disallow arg tags in static subprogs */
+               if (!is_global) {
+                       bpf_log(&env->log,
+                               "arg#%d type tag is not supported in static functions\n",
+                               arg_idx);
+                       return -EOPNOTSUPP;
+               }
+
+               if (strcmp(tag, "ctx") == 0) {
+                       *tags |= ARG_TAG_CTX;
+               } else if (strcmp(tag, "trusted") == 0) {
+                       *tags |= ARG_TAG_TRUSTED;
+               } else if (strcmp(tag, "untrusted") == 0) {
+                       *tags |= ARG_TAG_UNTRUSTED;
+               } else if (strcmp(tag, "nonnull") == 0) {
+                       *tags |= ARG_TAG_NONNULL;
+               } else if (strcmp(tag, "nullable") == 0) {
+                       *tags |= ARG_TAG_NULLABLE;
+               } else if (strcmp(tag, "arena") == 0) {
+                       *tags |= ARG_TAG_ARENA;
+               } else {
+                       bpf_log(&env->log, "arg#%d has unsupported set of tags\n", arg_idx);
+                       return -EOPNOTSUPP;
+               }
+       }
+       if (id != -ENOENT) {
+               bpf_log(&env->log, "arg#%d type tag fetching failure: %d\n", arg_idx, id);
+               return id;
+       }
+
+       return 0;
+}
+
+static int btf_scan_type_tags(struct bpf_verifier_env *env,
+                             const struct btf *btf, u32 type_id,
+                             u32 *tags)
+{
+       const struct btf_type *t;
+
+       /* Find the first pointer type in the chain. */
+       t = btf_type_skip_modifiers(btf, type_id, NULL);
+       if (!t || !btf_type_is_ptr(t))
+               return 0;
+
+       /* We got a pointer, get all associated type tags. */
+       t = btf_type_by_id(btf, t->type);
+       while (t && btf_type_is_type_tag(t)) {
+               const char *tag = __btf_name_by_offset(btf, t->name_off);
+
+               if (strcmp(tag, "arena") == 0) {
+                       *tags |= ARG_TAG_ARENA;
+               } else {
+                       bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n",
+                               tag);
+                       return -EOPNOTSUPP;
+               }
+
+               t = btf_type_by_id(btf, t->type);
+       }
+
+       return 0;
+}
+
 /* 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.
@@ -7820,6 +7898,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
        struct btf *btf = prog->aux->btf;
        const struct btf_param *args;
        const struct btf_type *t, *ref_t, *fn_t;
+       int err;
        u32 i, nargs, btf_id;
        const char *tname;
 
@@ -7903,42 +7982,13 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
         */
        for (i = 0; i < nargs; i++) {
                u32 tags = 0;
-               int id = btf_named_start_id(btf, false) - 1;
-
-               /* 'arg:<tag>' decl_tag takes precedence over derivation of
-                * register type from BTF type itself
-                */
-               while ((id = btf_find_next_decl_tag(btf, fn_t, i, "arg:", id)) > 0) {
-                       const struct btf_type *tag_t = btf_type_by_id(btf, id);
-                       const char *tag = __btf_name_by_offset(btf, tag_t->name_off) + 4;
-
-                       /* disallow arg tags in static subprogs */
-                       if (!is_global) {
-                               bpf_log(log, "arg#%d type tag is not supported in static functions\n", i);
-                               return -EOPNOTSUPP;
-                       }
+               err = btf_scan_decl_tags(env, btf, fn_t, i, is_global, &tags);
+               if (err)
+                       return err;
 
-                       if (strcmp(tag, "ctx") == 0) {
-                               tags |= ARG_TAG_CTX;
-                       } else if (strcmp(tag, "trusted") == 0) {
-                               tags |= ARG_TAG_TRUSTED;
-                       } else if (strcmp(tag, "untrusted") == 0) {
-                               tags |= ARG_TAG_UNTRUSTED;
-                       } else if (strcmp(tag, "nonnull") == 0) {
-                               tags |= ARG_TAG_NONNULL;
-                       } else if (strcmp(tag, "nullable") == 0) {
-                               tags |= ARG_TAG_NULLABLE;
-                       } else if (strcmp(tag, "arena") == 0) {
-                               tags |= ARG_TAG_ARENA;
-                       } else {
-                               bpf_log(log, "arg#%d has unsupported set of tags\n", i);
-                               return -EOPNOTSUPP;
-                       }
-               }
-               if (id != -ENOENT) {
-                       bpf_log(log, "arg#%d type tag fetching failure: %d\n", i, id);
-                       return id;
-               }
+               err = btf_scan_type_tags(env, btf, args[i].type, &tags);
+               if (err)
+                       return err;
 
                t = btf_type_by_id(btf, args[i].type);
                while (btf_type_is_modifier(t))