]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: support for void/primitive __arg_untrusted global func params
authorEduard Zingerman <eddyz87@gmail.com>
Fri, 4 Jul 2025 23:03:53 +0000 (16:03 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 7 Jul 2025 15:25:07 +0000 (08:25 -0700)
Allow specifying __arg_untrusted for void */char */int */long *
parameters. Treat such parameters as
PTR_TO_MEM|MEM_RDONLY|PTR_UNTRUSTED of size zero.
Intended usage is as follows:

  int memcmp(char *a __arg_untrusted, char *b __arg_untrusted, size_t n) {
    bpf_for(i, 0, n) {
      if (a[i] - b[i])      // load at any offset is allowed
        return a[i] - b[i];
    }
    return 0;
  }

Allocate register id for ARG_PTR_TO_MEM parameters only when
PTR_MAYBE_NULL is set. Register id for PTR_TO_MEM is used only to
propagate non-null status after conditionals.

Suggested-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250704230354.1323244-8-eddyz87@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/btf.h
kernel/bpf/btf.c
kernel/bpf/verifier.c

index a40beb9cf16040084acb5ba1146c397e6e3bc5a3..9eda6b113f9b4836c759eb7cd50b534231251710 100644 (file)
@@ -223,6 +223,7 @@ u32 btf_nr_types(const struct btf *btf);
 struct btf *btf_base_btf(const struct btf *btf);
 bool btf_type_is_i32(const struct btf_type *t);
 bool btf_type_is_i64(const struct btf_type *t);
+bool btf_type_is_primitive(const struct btf_type *t);
 bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
                           const struct btf_member *m,
                           u32 expected_offset, u32 expected_size);
index e0414d9f5e294e404c3cfa7b19bb8c9bc56786a5..2dd13eea7b0ea2b276202d8b792c2a1ed93dc4d9 100644 (file)
@@ -891,6 +891,12 @@ bool btf_type_is_i64(const struct btf_type *t)
        return btf_type_is_int(t) && __btf_type_int_is_regular(t, 8);
 }
 
+bool btf_type_is_primitive(const struct btf_type *t)
+{
+       return (btf_type_is_int(t) && btf_type_int_is_regular(t)) ||
+              btf_is_any_enum(t);
+}
+
 /*
  * Check that given struct member is a regular int with expected
  * offset and size.
@@ -7830,6 +7836,13 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
                                return -EINVAL;
                        }
 
+                       ref_t = btf_type_skip_modifiers(btf, t->type, NULL);
+                       if (btf_type_is_void(ref_t) || btf_type_is_primitive(ref_t)) {
+                               sub->args[i].arg_type = ARG_PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED;
+                               sub->args[i].mem_size = 0;
+                               continue;
+                       }
+
                        kern_type_id = btf_get_ptr_to_btf_id(log, i, btf, t);
                        if (kern_type_id < 0)
                                return kern_type_id;
@@ -7838,7 +7851,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
                        ref_t = btf_type_by_id(vmlinux_btf, kern_type_id);
                        if (!btf_type_is_struct(ref_t)) {
                                tname = __btf_name_by_offset(vmlinux_btf, t->name_off);
-                               bpf_log(log, "arg#%d has type %s '%s', but only struct types are allowed\n",
+                               bpf_log(log, "arg#%d has type %s '%s', but only struct or primitive types are allowed\n",
                                        i, btf_type_str(ref_t), tname);
                                return -EINVAL;
                        }
index 7af902c3ecc3f8c7a12a7947c5f7d9ff58d0c8a5..1e567fff6f232cd8635ba01d0a97edc72de7f7a6 100644 (file)
@@ -23152,11 +23152,12 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
                                __mark_dynptr_reg(reg, BPF_DYNPTR_TYPE_LOCAL, true, ++env->id_gen);
                        } else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) {
                                reg->type = PTR_TO_MEM;
-                               if (arg->arg_type & PTR_MAYBE_NULL)
-                                       reg->type |= PTR_MAYBE_NULL;
+                               reg->type |= arg->arg_type &
+                                            (PTR_MAYBE_NULL | PTR_UNTRUSTED | MEM_RDONLY);
                                mark_reg_known_zero(env, regs, i);
                                reg->mem_size = arg->mem_size;
-                               reg->id = ++env->id_gen;
+                               if (arg->arg_type & PTR_MAYBE_NULL)
+                                       reg->id = ++env->id_gen;
                        } else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) {
                                reg->type = PTR_TO_BTF_ID;
                                if (arg->arg_type & PTR_MAYBE_NULL)