Skip to content

Commit cd19f9c

Browse files
committed
bpf: Extend kfunc with PTR_TO_CTX, PTR_TO_MEM argument support
jira VULN-72 cve-pre CVE-2021-4204 commit-author Kumar Kartikeya Dwivedi <[email protected]> commit 3363bd0 upstream-diff Slight change due to previously backported commit 38f85a6 (bpf: Fix crash due to out of bounds access into reg2btf_ids.) 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 <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected] (cherry picked from commit 3363bd0) Signed-off-by: Brett Mastbergen <[email protected]>
1 parent ab77683 commit cd19f9c

File tree

1 file changed

+73
-21
lines changed

1 file changed

+73
-21
lines changed

kernel/bpf/btf.c

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5384,12 +5384,53 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = {
53845384
#endif
53855385
};
53865386

5387+
/* Returns true if struct is composed of scalars, 4 levels of nesting allowed */
5388+
static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log,
5389+
const struct btf *btf,
5390+
const struct btf_type *t, int rec)
5391+
{
5392+
const struct btf_type *member_type;
5393+
const struct btf_member *member;
5394+
u32 i;
5395+
5396+
if (!btf_type_is_struct(t))
5397+
return false;
5398+
5399+
for_each_member(i, t, member) {
5400+
const struct btf_array *array;
5401+
5402+
member_type = btf_type_skip_modifiers(btf, member->type, NULL);
5403+
if (btf_type_is_struct(member_type)) {
5404+
if (rec >= 3) {
5405+
bpf_log(log, "max struct nesting depth exceeded\n");
5406+
return false;
5407+
}
5408+
if (!__btf_type_is_scalar_struct(log, btf, member_type, rec + 1))
5409+
return false;
5410+
continue;
5411+
}
5412+
if (btf_type_is_array(member_type)) {
5413+
array = btf_type_array(member_type);
5414+
if (!array->nelems)
5415+
return false;
5416+
member_type = btf_type_skip_modifiers(btf, array->type, NULL);
5417+
if (!btf_type_is_scalar(member_type))
5418+
return false;
5419+
continue;
5420+
}
5421+
if (!btf_type_is_scalar(member_type))
5422+
return false;
5423+
}
5424+
return true;
5425+
}
5426+
53875427
static int btf_check_func_arg_match(struct bpf_verifier_env *env,
53885428
const struct btf *btf, u32 func_id,
53895429
struct bpf_reg_state *regs,
53905430
bool ptr_to_mem_ok)
53915431
{
53925432
struct bpf_verifier_log *log = &env->log;
5433+
bool is_kfunc = btf_is_kernel(btf);
53935434
const char *func_name, *ref_tname;
53945435
const struct btf_type *t, *ref_t;
53955436
const struct btf_param *args;
@@ -5442,7 +5483,20 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
54425483

54435484
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
54445485
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
5445-
if (btf_is_kernel(btf)) {
5486+
if (btf_get_prog_ctx_type(log, btf, t,
5487+
env->prog->type, i)) {
5488+
/* If function expects ctx type in BTF check that caller
5489+
* is passing PTR_TO_CTX.
5490+
*/
5491+
if (reg->type != PTR_TO_CTX) {
5492+
bpf_log(log,
5493+
"arg#%d expected pointer to ctx, but got %s\n",
5494+
i, btf_type_str(t));
5495+
return -EINVAL;
5496+
}
5497+
if (check_ctx_reg(env, reg, regno))
5498+
return -EINVAL;
5499+
} else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || reg2btf_ids[reg->type])) {
54465500
const struct btf_type *reg_ref_t;
54475501
const struct btf *reg_btf;
54485502
const char *reg_ref_tname;
@@ -5458,14 +5512,9 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
54585512
if (reg->type == PTR_TO_BTF_ID) {
54595513
reg_btf = reg->btf;
54605514
reg_ref_id = reg->btf_id;
5461-
} else if (reg2btf_ids[base_type(reg->type)]) {
5515+
} else {
54625516
reg_btf = btf_vmlinux;
54635517
reg_ref_id = *reg2btf_ids[base_type(reg->type)];
5464-
} else {
5465-
bpf_log(log, "kernel function %s args#%d expected pointer to %s %s but R%d is not a pointer to btf_id\n",
5466-
func_name, i,
5467-
btf_type_str(ref_t), ref_tname, regno);
5468-
return -EINVAL;
54695518
}
54705519

54715520
reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id,
@@ -5481,23 +5530,24 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
54815530
reg_ref_tname);
54825531
return -EINVAL;
54835532
}
5484-
} else if (btf_get_prog_ctx_type(log, btf, t,
5485-
env->prog->type, i)) {
5486-
/* If function expects ctx type in BTF check that caller
5487-
* is passing PTR_TO_CTX.
5488-
*/
5489-
if (reg->type != PTR_TO_CTX) {
5490-
bpf_log(log,
5491-
"arg#%d expected pointer to ctx, but got %s\n",
5492-
i, btf_type_str(t));
5493-
return -EINVAL;
5494-
}
5495-
if (check_ctx_reg(env, reg, regno))
5496-
return -EINVAL;
54975533
} else if (ptr_to_mem_ok) {
54985534
const struct btf_type *resolve_ret;
54995535
u32 type_size;
55005536

5537+
if (is_kfunc) {
5538+
/* Permit pointer to mem, but only when argument
5539+
* type is pointer to scalar, or struct composed
5540+
* (recursively) of scalars.
5541+
*/
5542+
if (!btf_type_is_scalar(ref_t) &&
5543+
!__btf_type_is_scalar_struct(log, btf, ref_t, 0)) {
5544+
bpf_log(log,
5545+
"arg#%d pointer type %s %s must point to scalar or struct with scalar\n",
5546+
i, btf_type_str(ref_t), ref_tname);
5547+
return -EINVAL;
5548+
}
5549+
}
5550+
55015551
resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
55025552
if (IS_ERR(resolve_ret)) {
55035553
bpf_log(log,
@@ -5510,6 +5560,8 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
55105560
if (check_mem_reg(env, reg, regno, type_size))
55115561
return -EINVAL;
55125562
} else {
5563+
bpf_log(log, "reg type unsupported for arg#%d %sfunction %s#%d\n", i,
5564+
is_kfunc ? "kernel " : "", func_name, func_id);
55135565
return -EINVAL;
55145566
}
55155567
}
@@ -5559,7 +5611,7 @@ int btf_check_kfunc_arg_match(struct bpf_verifier_env *env,
55595611
const struct btf *btf, u32 func_id,
55605612
struct bpf_reg_state *regs)
55615613
{
5562-
return btf_check_func_arg_match(env, btf, func_id, regs, false);
5614+
return btf_check_func_arg_match(env, btf, func_id, regs, true);
55635615
}
55645616

55655617
/* Convert BTF of a function into bpf_reg_state if possible

0 commit comments

Comments
 (0)