summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmil Tsalapatis <emil@etsalapatis.com>2026-06-01 20:41:16 -0400
committerAlexei Starovoitov <ast@kernel.org>2026-06-01 18:42:33 -0700
commit5ab4bc67d818ba27388d64cb9c52cb0c3bdac254 (patch)
tree10ccf070fdfa6144a30e1e7ccad881e70793de97
parenta0fa68d8ce759dbf6aaf19a043ddd77a2128c26c (diff)
verifier: parse BTF type tags for function arguments
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>
-rw-r--r--kernel/bpf/btf.c120
1 files changed, 85 insertions, 35 deletions
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index f429f6f58cb2..6fb3461e3ac2 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -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))