After ensuring that the verifier can recognize normal vs destructing objects, add bpf_kptr_free support, and then verify whether normal object can directly be freed, or whether it needs destruction. If already in destructing phase, ensure all fields have been destructed. Having this state in the verifier simplifies how we release resources for kptrs to local types with arbitrary fields considerably. The verifier just needs to ensure that destruction happens while program has ownership of object, and then it can just release the storage using bpf_kptr_free. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx> --- kernel/bpf/helpers.c | 6 ++++ kernel/bpf/verifier.c | 29 +++++++++++++++++++ .../testing/selftests/bpf/bpf_experimental.h | 8 +++++ 3 files changed, 43 insertions(+) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 8eee0793c7f1..4a6fffe401ae 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1731,6 +1731,11 @@ void bpf_list_head_init(struct bpf_list_head *head__clkptr) INIT_LIST_HEAD((struct list_head *)head__clkptr); } +void bpf_kptr_free(void *p__dlkptr) +{ + kfree(p__dlkptr); +} + __diag_pop(); BTF_SET8_START(tracing_btf_ids) @@ -1741,6 +1746,7 @@ BTF_ID_FLAGS(func, bpf_kptr_alloc, KF_ACQUIRE | KF_RET_NULL | __KF_RET_DYN_BTF) BTF_ID_FLAGS(func, bpf_list_node_init) BTF_ID_FLAGS(func, bpf_spin_lock_init) BTF_ID_FLAGS(func, bpf_list_head_init) +BTF_ID_FLAGS(func, bpf_kptr_free, KF_RELEASE) BTF_SET8_END(tracing_btf_ids) static const struct btf_kfunc_id_set tracing_kfunc_set = { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a5aa5de4b246..b1754fd69f7d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7812,6 +7812,7 @@ BTF_ID(func, bpf_kptr_alloc) BTF_ID(func, bpf_list_node_init) BTF_ID(func, bpf_spin_lock_init) BTF_ID(func, bpf_list_head_init) +BTF_ID(func, bpf_kptr_free) BTF_ID(struct, btf) /* empty entry */ enum bpf_special_kfuncs { @@ -7819,6 +7820,7 @@ enum bpf_special_kfuncs { KF_SPECIAL_bpf_list_node_init, KF_SPECIAL_bpf_spin_lock_init, KF_SPECIAL_bpf_list_head_init, + KF_SPECIAL_bpf_kptr_free, KF_SPECIAL_bpf_empty, KF_SPECIAL_MAX = KF_SPECIAL_bpf_empty, }; @@ -8156,6 +8158,33 @@ process_kf_arg_destructing_local_kptr(struct bpf_verifier_env *env, })); } + /* Handle bpf_kptr_free */ + if (is_kfunc_special(meta->btf, meta->func_id, bpf_kptr_free)) { + for (i = cnt - 1; i >= 0; i--) { + if (!fields[i].needs_destruction) + continue; + /* If a field needs destruction, it must be in + * destructed state when calling bpf_kptr_free. + */ + switch (local_kptr_get_state(reg, i)) { + case FIELD_STATE_CONSTRUCTED: + verbose(env, "'%s' field needs to be destructed before bpf_kptr_free\n", + fields[i].name); + return -EINVAL; + case FIELD_STATE_DESTRUCTED: + break; + case FIELD_STATE_UNKNOWN: + if (reg->type & OBJ_CONSTRUCTING) + break; + fallthrough; + default: + verbose(env, "verifier internal error: unknown field state\n"); + return -EFAULT; + } + } + return 0; + } + for (i = 0; i < cnt; i++) { bool mark_dtor = false, unmark_ctor = false; int j; diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index f0b6e92c6908..595e99d5cbc2 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -59,4 +59,12 @@ void bpf_spin_lock_init(struct bpf_spin_lock *node) __ksym; */ void bpf_list_head_init(struct bpf_list_head *node) __ksym; +/* Description + * Free a local kptr. All fields of local kptr that require destruction + * need to be in destructed state before this call is made. + * Returns + * Void. + */ +void bpf_kptr_free(void *kptr) __ksym; + #endif -- 2.34.1