bpf: Add a kfunc to type cast from bpf uapi ctx to kernel ctx

Implement bpf_cast_to_kern_ctx() kfunc which does a type cast
of a uapi ctx object to the corresponding kernel ctx. Previously
if users want to access some data available in kctx but not
in uapi ctx, bpf_probe_read_kernel() helper is needed.
The introduction of bpf_cast_to_kern_ctx() allows direct
memory access which makes code simpler and easier to understand.

Signed-off-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/r/20221120195432.3113982-1-yhs@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Yonghong Song 2022-11-20 11:54:32 -08:00 committed by Alexei Starovoitov
parent cfe1456440
commit fd264ca020
4 changed files with 53 additions and 0 deletions

View File

@ -487,6 +487,7 @@ const struct btf_member *
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
const struct btf_type *t, enum bpf_prog_type prog_type, const struct btf_type *t, enum bpf_prog_type prog_type,
int arg); int arg);
int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_type);
bool btf_types_are_same(const struct btf *btf1, u32 id1, bool btf_types_are_same(const struct btf *btf1, u32 id1,
const struct btf *btf2, u32 id2); const struct btf *btf2, u32 id2);
#else #else
@ -531,6 +532,10 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
{ {
return NULL; return NULL;
} }
static inline int get_kern_ctx_btf_id(struct bpf_verifier_log *log,
enum bpf_prog_type prog_type) {
return -EINVAL;
}
static inline bool btf_types_are_same(const struct btf *btf1, u32 id1, static inline bool btf_types_are_same(const struct btf *btf1, u32 id1,
const struct btf *btf2, u32 id2) const struct btf *btf2, u32 id2)
{ {

View File

@ -5603,6 +5603,26 @@ static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,
return kern_ctx_type->type; return kern_ctx_type->type;
} }
int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_type)
{
const struct btf_member *kctx_member;
const struct btf_type *conv_struct;
const struct btf_type *kctx_type;
u32 kctx_type_id;
conv_struct = bpf_ctx_convert.t;
/* get member for kernel ctx type */
kctx_member = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2 + 1;
kctx_type_id = kctx_member->type;
kctx_type = btf_type_by_id(btf_vmlinux, kctx_type_id);
if (!btf_type_is_struct(kctx_type)) {
bpf_log(log, "kern ctx type id %u is not a struct\n", kctx_type_id);
return -EINVAL;
}
return kctx_type_id;
}
BTF_ID_LIST(bpf_ctx_convert_btf_id) BTF_ID_LIST(bpf_ctx_convert_btf_id)
BTF_ID(struct, bpf_ctx_convert) BTF_ID(struct, bpf_ctx_convert)

View File

@ -1879,6 +1879,11 @@ void bpf_task_release(struct task_struct *p)
put_task_struct_rcu_user(p); put_task_struct_rcu_user(p);
} }
void *bpf_cast_to_kern_ctx(void *obj)
{
return obj;
}
__diag_pop(); __diag_pop();
BTF_SET8_START(generic_btf_ids) BTF_SET8_START(generic_btf_ids)
@ -1907,6 +1912,7 @@ BTF_ID(struct, task_struct)
BTF_ID(func, bpf_task_release) BTF_ID(func, bpf_task_release)
BTF_SET8_START(common_btf_ids) BTF_SET8_START(common_btf_ids)
BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx)
BTF_SET8_END(common_btf_ids) BTF_SET8_END(common_btf_ids)
static const struct btf_kfunc_id_set common_kfunc_set = { static const struct btf_kfunc_id_set common_kfunc_set = {

View File

@ -7907,6 +7907,7 @@ struct bpf_kfunc_call_arg_meta {
u32 ref_obj_id; u32 ref_obj_id;
u8 release_regno; u8 release_regno;
bool r0_rdonly; bool r0_rdonly;
u32 ret_btf_id;
u64 r0_size; u64 r0_size;
struct { struct {
u64 value; u64 value;
@ -8151,6 +8152,7 @@ enum special_kfunc_type {
KF_bpf_list_push_back, KF_bpf_list_push_back,
KF_bpf_list_pop_front, KF_bpf_list_pop_front,
KF_bpf_list_pop_back, KF_bpf_list_pop_back,
KF_bpf_cast_to_kern_ctx,
}; };
BTF_SET_START(special_kfunc_set) BTF_SET_START(special_kfunc_set)
@ -8160,6 +8162,7 @@ BTF_ID(func, bpf_list_push_front)
BTF_ID(func, bpf_list_push_back) BTF_ID(func, bpf_list_push_back)
BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_front)
BTF_ID(func, bpf_list_pop_back) BTF_ID(func, bpf_list_pop_back)
BTF_ID(func, bpf_cast_to_kern_ctx)
BTF_SET_END(special_kfunc_set) BTF_SET_END(special_kfunc_set)
BTF_ID_LIST(special_kfunc_list) BTF_ID_LIST(special_kfunc_list)
@ -8169,6 +8172,7 @@ BTF_ID(func, bpf_list_push_front)
BTF_ID(func, bpf_list_push_back) BTF_ID(func, bpf_list_push_back)
BTF_ID(func, bpf_list_pop_front) BTF_ID(func, bpf_list_pop_front)
BTF_ID(func, bpf_list_pop_back) BTF_ID(func, bpf_list_pop_back)
BTF_ID(func, bpf_cast_to_kern_ctx)
static enum kfunc_ptr_arg_type static enum kfunc_ptr_arg_type
get_kfunc_ptr_arg_type(struct bpf_verifier_env *env, get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
@ -8182,6 +8186,9 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
struct bpf_reg_state *reg = &regs[regno]; struct bpf_reg_state *reg = &regs[regno];
bool arg_mem_size = false; bool arg_mem_size = false;
if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx])
return KF_ARG_PTR_TO_CTX;
/* In this function, we verify the kfunc's BTF as per the argument type, /* In this function, we verify the kfunc's BTF as per the argument type,
* leaving the rest of the verification with respect to the register * leaving the rest of the verification with respect to the register
* type to our caller. When a set of conditions hold in the BTF type of * type to our caller. When a set of conditions hold in the BTF type of
@ -8668,6 +8675,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t)); verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t));
return -EINVAL; return -EINVAL;
} }
if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
if (ret < 0)
return -EINVAL;
meta->ret_btf_id = ret;
}
break; break;
case KF_ARG_PTR_TO_ALLOC_BTF_ID: case KF_ARG_PTR_TO_ALLOC_BTF_ID:
if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
@ -8922,6 +8936,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
regs[BPF_REG_0].btf = field->list_head.btf; regs[BPF_REG_0].btf = field->list_head.btf;
regs[BPF_REG_0].btf_id = field->list_head.value_btf_id; regs[BPF_REG_0].btf_id = field->list_head.value_btf_id;
regs[BPF_REG_0].off = field->list_head.node_offset; regs[BPF_REG_0].off = field->list_head.node_offset;
} else if (meta.func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED;
regs[BPF_REG_0].btf = desc_btf;
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
} else { } else {
verbose(env, "kernel function %s unhandled dynamic return type\n", verbose(env, "kernel function %s unhandled dynamic return type\n",
meta.func_name); meta.func_name);
@ -15175,6 +15194,9 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
insn_buf[1] = addr[1]; insn_buf[1] = addr[1];
insn_buf[2] = *insn; insn_buf[2] = *insn;
*cnt = 3; *cnt = 3;
} else if (desc->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
insn_buf[0] = BPF_MOV64_REG(BPF_REG_0, BPF_REG_1);
*cnt = 1;
} }
return 0; return 0;
} }