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

bpf: reuse subprog argument parsing logic for subprog call checks

Remove duplicated BTF parsing logic when it comes to subprog call check.
Instead, use (potentially cached) results of btf_prepare_func_args() to
abstract away expectations of each subprog argument in generic terms
(e.g., "this is pointer to context", or "this is a pointer to memory of
size X"), and then use those simple high-level argument type
expectations to validate actual register states to check if they match
expectations.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231215011334.2307144-6-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
f18c3d88 c5a72447

+31 -81
+30 -80
kernel/bpf/verifier.c
··· 9249 9249 return err; 9250 9250 } 9251 9251 9252 - static int btf_check_func_arg_match(struct bpf_verifier_env *env, 9253 - const struct btf *btf, u32 func_id, 9254 - struct bpf_reg_state *regs, 9255 - bool ptr_to_mem_ok) 9252 + static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, 9253 + const struct btf *btf, 9254 + struct bpf_reg_state *regs) 9256 9255 { 9257 - enum bpf_prog_type prog_type = resolve_prog_type(env->prog); 9256 + struct bpf_subprog_info *sub = subprog_info(env, subprog); 9258 9257 struct bpf_verifier_log *log = &env->log; 9259 - const char *func_name, *ref_tname; 9260 - const struct btf_type *t, *ref_t; 9261 - const struct btf_param *args; 9262 - u32 i, nargs, ref_id; 9258 + u32 i; 9263 9259 int ret; 9264 9260 9265 - t = btf_type_by_id(btf, func_id); 9266 - if (!t || !btf_type_is_func(t)) { 9267 - /* These checks were already done by the verifier while loading 9268 - * struct bpf_func_info or in add_kfunc_call(). 9269 - */ 9270 - bpf_log(log, "BTF of func_id %u doesn't point to KIND_FUNC\n", 9271 - func_id); 9272 - return -EFAULT; 9273 - } 9274 - func_name = btf_name_by_offset(btf, t->name_off); 9275 - 9276 - t = btf_type_by_id(btf, t->type); 9277 - if (!t || !btf_type_is_func_proto(t)) { 9278 - bpf_log(log, "Invalid BTF of func %s\n", func_name); 9279 - return -EFAULT; 9280 - } 9281 - args = (const struct btf_param *)(t + 1); 9282 - nargs = btf_type_vlen(t); 9283 - if (nargs > MAX_BPF_FUNC_REG_ARGS) { 9284 - bpf_log(log, "Function %s has %d > %d args\n", func_name, nargs, 9285 - MAX_BPF_FUNC_REG_ARGS); 9286 - return -EINVAL; 9287 - } 9261 + ret = btf_prepare_func_args(env, subprog); 9262 + if (ret) 9263 + return ret; 9288 9264 9289 9265 /* check that BTF function arguments match actual types that the 9290 9266 * verifier sees. 9291 9267 */ 9292 - for (i = 0; i < nargs; i++) { 9293 - enum bpf_arg_type arg_type = ARG_DONTCARE; 9268 + for (i = 0; i < sub->arg_cnt; i++) { 9294 9269 u32 regno = i + 1; 9295 9270 struct bpf_reg_state *reg = &regs[regno]; 9271 + struct bpf_subprog_arg_info *arg = &sub->args[i]; 9296 9272 9297 - t = btf_type_skip_modifiers(btf, args[i].type, NULL); 9298 - if (btf_type_is_scalar(t)) { 9299 - if (reg->type == SCALAR_VALUE) 9300 - continue; 9301 - bpf_log(log, "R%d is not a scalar\n", regno); 9302 - return -EINVAL; 9303 - } 9304 - 9305 - if (!btf_type_is_ptr(t)) { 9306 - bpf_log(log, "Unrecognized arg#%d type %s\n", 9307 - i, btf_type_str(t)); 9308 - return -EINVAL; 9309 - } 9310 - 9311 - ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id); 9312 - ref_tname = btf_name_by_offset(btf, ref_t->name_off); 9313 - 9314 - ret = check_func_arg_reg_off(env, reg, regno, arg_type); 9315 - if (ret < 0) 9316 - return ret; 9317 - 9318 - if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) { 9273 + if (arg->arg_type == ARG_ANYTHING) { 9274 + if (reg->type != SCALAR_VALUE) { 9275 + bpf_log(log, "R%d is not a scalar\n", regno); 9276 + return -EINVAL; 9277 + } 9278 + } else if (arg->arg_type == ARG_PTR_TO_CTX) { 9279 + ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE); 9280 + if (ret < 0) 9281 + return ret; 9319 9282 /* If function expects ctx type in BTF check that caller 9320 9283 * is passing PTR_TO_CTX. 9321 9284 */ 9322 9285 if (reg->type != PTR_TO_CTX) { 9323 - bpf_log(log, 9324 - "arg#%d expected pointer to ctx, but got %s\n", 9325 - i, btf_type_str(t)); 9286 + bpf_log(log, "arg#%d expects pointer to ctx\n", i); 9326 9287 return -EINVAL; 9327 9288 } 9328 - } else if (ptr_to_mem_ok) { 9329 - const struct btf_type *resolve_ret; 9330 - u32 type_size; 9289 + } else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) { 9290 + ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE); 9291 + if (ret < 0) 9292 + return ret; 9331 9293 9332 - resolve_ret = btf_resolve_size(btf, ref_t, &type_size); 9333 - if (IS_ERR(resolve_ret)) { 9334 - bpf_log(log, 9335 - "arg#%d reference type('%s %s') size cannot be determined: %ld\n", 9336 - i, btf_type_str(ref_t), ref_tname, 9337 - PTR_ERR(resolve_ret)); 9338 - return -EINVAL; 9339 - } 9340 - 9341 - if (check_mem_reg(env, reg, regno, type_size)) 9294 + if (check_mem_reg(env, reg, regno, arg->mem_size)) 9342 9295 return -EINVAL; 9343 9296 } else { 9344 - bpf_log(log, "reg type unsupported for arg#%d function %s#%d\n", i, 9345 - func_name, func_id); 9346 - return -EINVAL; 9297 + bpf_log(log, "verifier bug: unrecognized arg#%d type %d\n", 9298 + i, arg->arg_type); 9299 + return -EFAULT; 9347 9300 } 9348 9301 } 9349 9302 ··· 9311 9358 * Only PTR_TO_CTX and SCALAR_VALUE states are recognized. 9312 9359 */ 9313 9360 static int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog, 9314 - struct bpf_reg_state *regs) 9361 + struct bpf_reg_state *regs) 9315 9362 { 9316 9363 struct bpf_prog *prog = env->prog; 9317 9364 struct btf *btf = prog->aux->btf; 9318 - bool is_global; 9319 9365 u32 btf_id; 9320 9366 int err; 9321 9367 ··· 9328 9376 if (prog->aux->func_info_aux[subprog].unreliable) 9329 9377 return -EINVAL; 9330 9378 9331 - is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL; 9332 - err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global); 9333 - 9379 + err = btf_check_func_arg_match(env, subprog, btf, regs); 9334 9380 /* Compiler optimizations can remove arguments from static functions 9335 9381 * or mismatched type can be passed into a global function. 9336 9382 * In such cases mark the function as unreliable from BTF point of view.
+1 -1
tools/testing/selftests/bpf/progs/test_global_func5.c
··· 26 26 } 27 27 28 28 SEC("tc") 29 - __failure __msg("expected pointer to ctx, but got PTR") 29 + __failure __msg("expects pointer to ctx") 30 30 int global_func5(struct __sk_buff *skb) 31 31 { 32 32 return f1(skb) + f2(2, skb) + f3(3, skb);