Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Recognize btf_decl_tag("arg: Arena") as PTR_TO_ARENA.

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

authored by

Alexei Starovoitov and committed by
Andrii Nakryiko
2edc3de6 6082b6c3

+31 -4
+1
include/linux/bpf.h
··· 712 712 * on eBPF program stack 713 713 */ 714 714 ARG_PTR_TO_MEM, /* pointer to valid memory (stack, packet, map value) */ 715 + ARG_PTR_TO_ARENA, 715 716 716 717 ARG_CONST_SIZE, /* number of bytes accessed from memory */ 717 718 ARG_CONST_SIZE_OR_ZERO, /* number of bytes accessed from memory or 0 */
+15 -4
kernel/bpf/btf.c
··· 7111 7111 } 7112 7112 7113 7113 enum btf_arg_tag { 7114 - ARG_TAG_CTX = 0x1, 7115 - ARG_TAG_NONNULL = 0x2, 7116 - ARG_TAG_TRUSTED = 0x4, 7117 - ARG_TAG_NULLABLE = 0x8, 7114 + ARG_TAG_CTX = BIT_ULL(0), 7115 + ARG_TAG_NONNULL = BIT_ULL(1), 7116 + ARG_TAG_TRUSTED = BIT_ULL(2), 7117 + ARG_TAG_NULLABLE = BIT_ULL(3), 7118 + ARG_TAG_ARENA = BIT_ULL(4), 7118 7119 }; 7119 7120 7120 7121 /* Process BTF of a function to produce high-level expectation of function ··· 7227 7226 tags |= ARG_TAG_NONNULL; 7228 7227 } else if (strcmp(tag, "nullable") == 0) { 7229 7228 tags |= ARG_TAG_NULLABLE; 7229 + } else if (strcmp(tag, "arena") == 0) { 7230 + tags |= ARG_TAG_ARENA; 7230 7231 } else { 7231 7232 bpf_log(log, "arg#%d has unsupported set of tags\n", i); 7232 7233 return -EOPNOTSUPP; ··· 7281 7278 if (tags & ARG_TAG_NULLABLE) 7282 7279 sub->args[i].arg_type |= PTR_MAYBE_NULL; 7283 7280 sub->args[i].btf_id = kern_type_id; 7281 + continue; 7282 + } 7283 + if (tags & ARG_TAG_ARENA) { 7284 + if (tags & ~ARG_TAG_ARENA) { 7285 + bpf_log(log, "arg#%d arena cannot be combined with any other tags\n", i); 7286 + return -EINVAL; 7287 + } 7288 + sub->args[i].arg_type = ARG_PTR_TO_ARENA; 7284 7289 continue; 7285 7290 } 7286 7291 if (is_global) { /* generic user data pointer */
+15
kernel/bpf/verifier.c
··· 9379 9379 bpf_log(log, "arg#%d is expected to be non-NULL\n", i); 9380 9380 return -EINVAL; 9381 9381 } 9382 + } else if (base_type(arg->arg_type) == ARG_PTR_TO_ARENA) { 9383 + /* 9384 + * Can pass any value and the kernel won't crash, but 9385 + * only PTR_TO_ARENA or SCALAR make sense. Everything 9386 + * else is a bug in the bpf program. Point it out to 9387 + * the user at the verification time instead of 9388 + * run-time debug nightmare. 9389 + */ 9390 + if (reg->type != PTR_TO_ARENA && reg->type != SCALAR_VALUE) { 9391 + bpf_log(log, "R%d is not a pointer to arena or scalar.\n", regno); 9392 + return -EINVAL; 9393 + } 9382 9394 } else if (arg->arg_type == (ARG_PTR_TO_DYNPTR | MEM_RDONLY)) { 9383 9395 ret = process_dynptr_func(env, regno, -1, arg->arg_type, 0); 9384 9396 if (ret) ··· 20460 20448 reg->btf = bpf_get_btf_vmlinux(); /* can't fail at this point */ 20461 20449 reg->btf_id = arg->btf_id; 20462 20450 reg->id = ++env->id_gen; 20451 + } else if (base_type(arg->arg_type) == ARG_PTR_TO_ARENA) { 20452 + /* caller can pass either PTR_TO_ARENA or SCALAR */ 20453 + mark_reg_unknown(env, regs, i); 20463 20454 } else { 20464 20455 WARN_ONCE(1, "BUG: unhandled arg#%d type %d\n", 20465 20456 i - BPF_REG_1, arg->arg_type);