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

Configure Feed

Select the types of activity you want to include in your feed.

bpf: Extend kfunc with PTR_TO_CTX, PTR_TO_MEM argument support

Allow passing PTR_TO_CTX, if the kfunc expects a matching struct type,
and punt to PTR_TO_MEM block if reg->type does not fall in one of
PTR_TO_BTF_ID or PTR_TO_SOCK* types. This will be used by future commits
to get access to XDP and TC PTR_TO_CTX, and pass various data (flags,
l4proto, netns_id, etc.) encoded in opts struct passed as pointer to
kfunc.

For PTR_TO_MEM support, arguments are currently limited to pointer to
scalar, or pointer to struct composed of scalars. This is done so that
unsafe scenarios (like passing PTR_TO_MEM where PTR_TO_BTF_ID of
in-kernel valid structure is expected, which may have pointers) are
avoided. Since the argument checking happens basd on argument register
type, it is not easy to ascertain what the expected type is. In the
future, support for PTR_TO_MEM for kfunc can be extended to serve other
usecases. The struct type whose pointer is passed in may have maximum
nesting depth of 4, all recursively composed of scalars or struct with
scalars.

Future commits will add negative tests that check whether these
restrictions imposed for kfunc arguments are duly rejected by BPF
verifier or not.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20211217015031.1278167-4-memxor@gmail.com

authored by

Kumar Kartikeya Dwivedi and committed by
Alexei Starovoitov
3363bd0c 7f16d2aa

+73 -21
+73 -21
kernel/bpf/btf.c
··· 5576 5576 #endif 5577 5577 }; 5578 5578 5579 + /* Returns true if struct is composed of scalars, 4 levels of nesting allowed */ 5580 + static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log, 5581 + const struct btf *btf, 5582 + const struct btf_type *t, int rec) 5583 + { 5584 + const struct btf_type *member_type; 5585 + const struct btf_member *member; 5586 + u32 i; 5587 + 5588 + if (!btf_type_is_struct(t)) 5589 + return false; 5590 + 5591 + for_each_member(i, t, member) { 5592 + const struct btf_array *array; 5593 + 5594 + member_type = btf_type_skip_modifiers(btf, member->type, NULL); 5595 + if (btf_type_is_struct(member_type)) { 5596 + if (rec >= 3) { 5597 + bpf_log(log, "max struct nesting depth exceeded\n"); 5598 + return false; 5599 + } 5600 + if (!__btf_type_is_scalar_struct(log, btf, member_type, rec + 1)) 5601 + return false; 5602 + continue; 5603 + } 5604 + if (btf_type_is_array(member_type)) { 5605 + array = btf_type_array(member_type); 5606 + if (!array->nelems) 5607 + return false; 5608 + member_type = btf_type_skip_modifiers(btf, array->type, NULL); 5609 + if (!btf_type_is_scalar(member_type)) 5610 + return false; 5611 + continue; 5612 + } 5613 + if (!btf_type_is_scalar(member_type)) 5614 + return false; 5615 + } 5616 + return true; 5617 + } 5618 + 5579 5619 static int btf_check_func_arg_match(struct bpf_verifier_env *env, 5580 5620 const struct btf *btf, u32 func_id, 5581 5621 struct bpf_reg_state *regs, 5582 5622 bool ptr_to_mem_ok) 5583 5623 { 5584 5624 struct bpf_verifier_log *log = &env->log; 5625 + bool is_kfunc = btf_is_kernel(btf); 5585 5626 const char *func_name, *ref_tname; 5586 5627 const struct btf_type *t, *ref_t; 5587 5628 const struct btf_param *args; ··· 5675 5634 5676 5635 ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id); 5677 5636 ref_tname = btf_name_by_offset(btf, ref_t->name_off); 5678 - if (btf_is_kernel(btf)) { 5637 + if (btf_get_prog_ctx_type(log, btf, t, 5638 + env->prog->type, i)) { 5639 + /* If function expects ctx type in BTF check that caller 5640 + * is passing PTR_TO_CTX. 5641 + */ 5642 + if (reg->type != PTR_TO_CTX) { 5643 + bpf_log(log, 5644 + "arg#%d expected pointer to ctx, but got %s\n", 5645 + i, btf_type_str(t)); 5646 + return -EINVAL; 5647 + } 5648 + if (check_ctx_reg(env, reg, regno)) 5649 + return -EINVAL; 5650 + } else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || reg2btf_ids[reg->type])) { 5679 5651 const struct btf_type *reg_ref_t; 5680 5652 const struct btf *reg_btf; 5681 5653 const char *reg_ref_tname; ··· 5704 5650 if (reg->type == PTR_TO_BTF_ID) { 5705 5651 reg_btf = reg->btf; 5706 5652 reg_ref_id = reg->btf_id; 5707 - } else if (reg2btf_ids[reg->type]) { 5653 + } else { 5708 5654 reg_btf = btf_vmlinux; 5709 5655 reg_ref_id = *reg2btf_ids[reg->type]; 5710 - } else { 5711 - bpf_log(log, "kernel function %s args#%d expected pointer to %s %s but R%d is not a pointer to btf_id\n", 5712 - func_name, i, 5713 - btf_type_str(ref_t), ref_tname, regno); 5714 - return -EINVAL; 5715 5656 } 5716 5657 5717 5658 reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, ··· 5722 5673 reg_ref_tname); 5723 5674 return -EINVAL; 5724 5675 } 5725 - } else if (btf_get_prog_ctx_type(log, btf, t, 5726 - env->prog->type, i)) { 5727 - /* If function expects ctx type in BTF check that caller 5728 - * is passing PTR_TO_CTX. 5729 - */ 5730 - if (reg->type != PTR_TO_CTX) { 5731 - bpf_log(log, 5732 - "arg#%d expected pointer to ctx, but got %s\n", 5733 - i, btf_type_str(t)); 5734 - return -EINVAL; 5735 - } 5736 - if (check_ctx_reg(env, reg, regno)) 5737 - return -EINVAL; 5738 5676 } else if (ptr_to_mem_ok) { 5739 5677 const struct btf_type *resolve_ret; 5740 5678 u32 type_size; 5679 + 5680 + if (is_kfunc) { 5681 + /* Permit pointer to mem, but only when argument 5682 + * type is pointer to scalar, or struct composed 5683 + * (recursively) of scalars. 5684 + */ 5685 + if (!btf_type_is_scalar(ref_t) && 5686 + !__btf_type_is_scalar_struct(log, btf, ref_t, 0)) { 5687 + bpf_log(log, 5688 + "arg#%d pointer type %s %s must point to scalar or struct with scalar\n", 5689 + i, btf_type_str(ref_t), ref_tname); 5690 + return -EINVAL; 5691 + } 5692 + } 5741 5693 5742 5694 resolve_ret = btf_resolve_size(btf, ref_t, &type_size); 5743 5695 if (IS_ERR(resolve_ret)) { ··· 5752 5702 if (check_mem_reg(env, reg, regno, type_size)) 5753 5703 return -EINVAL; 5754 5704 } else { 5705 + bpf_log(log, "reg type unsupported for arg#%d %sfunction %s#%d\n", i, 5706 + is_kfunc ? "kernel " : "", func_name, func_id); 5755 5707 return -EINVAL; 5756 5708 } 5757 5709 } ··· 5803 5751 const struct btf *btf, u32 func_id, 5804 5752 struct bpf_reg_state *regs) 5805 5753 { 5806 - return btf_check_func_arg_match(env, btf, func_id, regs, false); 5754 + return btf_check_func_arg_match(env, btf, func_id, regs, true); 5807 5755 } 5808 5756 5809 5757 /* Convert BTF of a function into bpf_reg_state if possible