bpf: Recognize btf_decl_tag("arg: Arena") as PTR_TO_ARENA.
authorAlexei Starovoitov <ast@kernel.org>
Fri, 8 Mar 2024 01:08:04 +0000 (17:08 -0800)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 11 Mar 2024 22:37:24 +0000 (15:37 -0700)
In global bpf functions recognize btf_decl_tag("arg:arena") as PTR_TO_ARENA.

Note, when the verifier sees:

__weak void foo(struct bar *p)

it recognizes 'p' as PTR_TO_MEM and 'struct bar' has to be a struct with scalars.
Hence the only way to use arena pointers in global functions is to tag them with "arg:arena".

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/bpf/20240308010812.89848-7-alexei.starovoitov@gmail.com
include/linux/bpf.h
kernel/bpf/btf.c
kernel/bpf/verifier.c

index d0c836ba009dd9f978beefb49f57e8854f78dddf..08ad265cb195979903f4f200193672cdf5214517 100644 (file)
@@ -712,6 +712,7 @@ enum bpf_arg_type {
         * on eBPF program stack
         */
        ARG_PTR_TO_MEM,         /* pointer to valid memory (stack, packet, map value) */
+       ARG_PTR_TO_ARENA,
 
        ARG_CONST_SIZE,         /* number of bytes accessed from memory */
        ARG_CONST_SIZE_OR_ZERO, /* number of bytes accessed from memory or 0 */
index 170d017e8e4afc1939ed2f82642b87cb8ee4f4aa..90c4a32d89ff36da3df48bd26b71c42a936fe2e4 100644 (file)
@@ -7111,10 +7111,11 @@ cand_cache_unlock:
 }
 
 enum btf_arg_tag {
-       ARG_TAG_CTX = 0x1,
-       ARG_TAG_NONNULL = 0x2,
-       ARG_TAG_TRUSTED = 0x4,
-       ARG_TAG_NULLABLE = 0x8,
+       ARG_TAG_CTX      = BIT_ULL(0),
+       ARG_TAG_NONNULL  = BIT_ULL(1),
+       ARG_TAG_TRUSTED  = BIT_ULL(2),
+       ARG_TAG_NULLABLE = BIT_ULL(3),
+       ARG_TAG_ARENA    = BIT_ULL(4),
 };
 
 /* Process BTF of a function to produce high-level expectation of function
@@ -7226,6 +7227,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
                                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;
@@ -7280,6 +7283,14 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
                        sub->args[i].btf_id = kern_type_id;
                        continue;
                }
+               if (tags & ARG_TAG_ARENA) {
+                       if (tags & ~ARG_TAG_ARENA) {
+                               bpf_log(log, "arg#%d arena cannot be combined with any other tags\n", i);
+                               return -EINVAL;
+                       }
+                       sub->args[i].arg_type = ARG_PTR_TO_ARENA;
+                       continue;
+               }
                if (is_global) { /* generic user data pointer */
                        u32 mem_size;
 
index 1358e20d315adf03e1d9230cf70384f804155f46..d64f7a9b60e81e5387742fefc611a0ef89a9bf2b 100644 (file)
@@ -9379,6 +9379,18 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
                                bpf_log(log, "arg#%d is expected to be non-NULL\n", i);
                                return -EINVAL;
                        }
+               } else if (base_type(arg->arg_type) == ARG_PTR_TO_ARENA) {
+                       /*
+                        * Can pass any value and the kernel won't crash, but
+                        * only PTR_TO_ARENA or SCALAR make sense. Everything
+                        * else is a bug in the bpf program. Point it out to
+                        * the user at the verification time instead of
+                        * run-time debug nightmare.
+                        */
+                       if (reg->type != PTR_TO_ARENA && reg->type != SCALAR_VALUE) {
+                               bpf_log(log, "R%d is not a pointer to arena or scalar.\n", regno);
+                               return -EINVAL;
+                       }
                } else if (arg->arg_type == (ARG_PTR_TO_DYNPTR | MEM_RDONLY)) {
                        ret = process_dynptr_func(env, regno, -1, arg->arg_type, 0);
                        if (ret)
@@ -20448,6 +20460,9 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
                                reg->btf = bpf_get_btf_vmlinux(); /* can't fail at this point */
                                reg->btf_id = arg->btf_id;
                                reg->id = ++env->id_gen;
+                       } else if (base_type(arg->arg_type) == ARG_PTR_TO_ARENA) {
+                               /* caller can pass either PTR_TO_ARENA or SCALAR */
+                               mark_reg_unknown(env, regs, i);
                        } else {
                                WARN_ONCE(1, "BUG: unhandled arg#%d type %d\n",
                                          i - BPF_REG_1, arg->arg_type);