Skip to content

Commit b0966c7

Browse files
davemarchevskyAlexei Starovoitov
authored andcommitted
bpf: Support bpf_kptr_xchg into local kptr
Currently, users can only stash kptr into map values with bpf_kptr_xchg(). This patch further supports stashing kptr into local kptr by adding local kptr as a valid destination type. When stashing into local kptr, btf_record in program BTF is used instead of btf_record in map to search for the btf_field of the local kptr. The local kptr specific checks in check_reg_type() only apply when the source argument of bpf_kptr_xchg() is local kptr. Therefore, we make the scope of the check explicit as the destination now can also be local kptr. Acked-by: Martin KaFai Lau <[email protected]> Signed-off-by: Dave Marchevsky <[email protected]> Signed-off-by: Amery Hung <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent d59232a commit b0966c7

File tree

3 files changed

+37
-20
lines changed

3 files changed

+37
-20
lines changed

include/uapi/linux/bpf.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5519,11 +5519,12 @@ union bpf_attr {
55195519
* **-EOPNOTSUPP** if the hash calculation failed or **-EINVAL** if
55205520
* invalid arguments are passed.
55215521
*
5522-
* void *bpf_kptr_xchg(void *map_value, void *ptr)
5522+
* void *bpf_kptr_xchg(void *dst, void *ptr)
55235523
* Description
5524-
* Exchange kptr at pointer *map_value* with *ptr*, and return the
5525-
* old value. *ptr* can be NULL, otherwise it must be a referenced
5526-
* pointer which will be released when this helper is called.
5524+
* Exchange kptr at pointer *dst* with *ptr*, and return the old value.
5525+
* *dst* can be map value or local kptr. *ptr* can be NULL, otherwise
5526+
* it must be a referenced pointer which will be released when this helper
5527+
* is called.
55275528
* Return
55285529
* The old value of kptr (which can be NULL). The returned pointer
55295530
* if not NULL, is a reference which must be released using its

kernel/bpf/helpers.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,9 +1619,9 @@ void bpf_wq_cancel_and_free(void *val)
16191619
schedule_work(&work->delete_work);
16201620
}
16211621

1622-
BPF_CALL_2(bpf_kptr_xchg, void *, map_value, void *, ptr)
1622+
BPF_CALL_2(bpf_kptr_xchg, void *, dst, void *, ptr)
16231623
{
1624-
unsigned long *kptr = map_value;
1624+
unsigned long *kptr = dst;
16251625

16261626
/* This helper may be inlined by verifier. */
16271627
return xchg(kptr, (unsigned long)ptr);

kernel/bpf/verifier.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7803,29 +7803,38 @@ static int process_kptr_func(struct bpf_verifier_env *env, int regno,
78037803
struct bpf_call_arg_meta *meta)
78047804
{
78057805
struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
7806-
struct bpf_map *map_ptr = reg->map_ptr;
78077806
struct btf_field *kptr_field;
7807+
struct bpf_map *map_ptr;
7808+
struct btf_record *rec;
78087809
u32 kptr_off;
78097810

7811+
if (type_is_ptr_alloc_obj(reg->type)) {
7812+
rec = reg_btf_record(reg);
7813+
} else { /* PTR_TO_MAP_VALUE */
7814+
map_ptr = reg->map_ptr;
7815+
if (!map_ptr->btf) {
7816+
verbose(env, "map '%s' has to have BTF in order to use bpf_kptr_xchg\n",
7817+
map_ptr->name);
7818+
return -EINVAL;
7819+
}
7820+
rec = map_ptr->record;
7821+
meta->map_ptr = map_ptr;
7822+
}
7823+
78107824
if (!tnum_is_const(reg->var_off)) {
78117825
verbose(env,
78127826
"R%d doesn't have constant offset. kptr has to be at the constant offset\n",
78137827
regno);
78147828
return -EINVAL;
78157829
}
7816-
if (!map_ptr->btf) {
7817-
verbose(env, "map '%s' has to have BTF in order to use bpf_kptr_xchg\n",
7818-
map_ptr->name);
7819-
return -EINVAL;
7820-
}
7821-
if (!btf_record_has_field(map_ptr->record, BPF_KPTR)) {
7822-
verbose(env, "map '%s' has no valid kptr\n", map_ptr->name);
7830+
7831+
if (!btf_record_has_field(rec, BPF_KPTR)) {
7832+
verbose(env, "R%d has no valid kptr\n", regno);
78237833
return -EINVAL;
78247834
}
78257835

7826-
meta->map_ptr = map_ptr;
78277836
kptr_off = reg->off + reg->var_off.value;
7828-
kptr_field = btf_record_find(map_ptr->record, kptr_off, BPF_KPTR);
7837+
kptr_field = btf_record_find(rec, kptr_off, BPF_KPTR);
78297838
if (!kptr_field) {
78307839
verbose(env, "off=%d doesn't point to kptr\n", kptr_off);
78317840
return -EACCES;
@@ -8412,7 +8421,12 @@ static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
84128421
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
84138422
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
84148423
static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE } };
8415-
static const struct bpf_reg_types kptr_xchg_dest_types = { .types = { PTR_TO_MAP_VALUE } };
8424+
static const struct bpf_reg_types kptr_xchg_dest_types = {
8425+
.types = {
8426+
PTR_TO_MAP_VALUE,
8427+
PTR_TO_BTF_ID | MEM_ALLOC
8428+
}
8429+
};
84168430
static const struct bpf_reg_types dynptr_types = {
84178431
.types = {
84188432
PTR_TO_STACK,
@@ -8483,7 +8497,8 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
84838497
if (base_type(arg_type) == ARG_PTR_TO_MEM)
84848498
type &= ~DYNPTR_TYPE_FLAG_MASK;
84858499

8486-
if (meta->func_id == BPF_FUNC_kptr_xchg && type_is_alloc(type)) {
8500+
/* Local kptr types are allowed as the source argument of bpf_kptr_xchg */
8501+
if (meta->func_id == BPF_FUNC_kptr_xchg && type_is_alloc(type) && regno == BPF_REG_2) {
84878502
type &= ~MEM_ALLOC;
84888503
type &= ~MEM_PERCPU;
84898504
}
@@ -8576,7 +8591,8 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
85768591
verbose(env, "verifier internal error: unimplemented handling of MEM_ALLOC\n");
85778592
return -EFAULT;
85788593
}
8579-
if (meta->func_id == BPF_FUNC_kptr_xchg) {
8594+
/* Check if local kptr in src arg matches kptr in dst arg */
8595+
if (meta->func_id == BPF_FUNC_kptr_xchg && regno == BPF_REG_2) {
85808596
if (map_kptr_match_type(env, meta->kptr_field, reg, regno))
85818597
return -EACCES;
85828598
}
@@ -8887,7 +8903,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
88878903
meta->release_regno = regno;
88888904
}
88898905

8890-
if (reg->ref_obj_id) {
8906+
if (reg->ref_obj_id && base_type(arg_type) != ARG_KPTR_XCHG_DEST) {
88918907
if (meta->ref_obj_id) {
88928908
verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
88938909
regno, reg->ref_obj_id,

0 commit comments

Comments
 (0)