From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Martin KaFai Lau <martin.lau@kernel.org>,
Dave Marchevsky <davemarchevsky@meta.com>
Subject: Re: [PATCH bpf-next v7 17/26] bpf: Introduce bpf_obj_new
Date: Tue, 15 Nov 2022 22:23:41 +0530 [thread overview]
Message-ID: <20221115165341.7y4hmnkfexjbo6oe@apollo> (raw)
In-Reply-To: <20221115061912.pcgjnx427dn6aaq2@macbook-pro-5.dhcp.thefacebook.com>
On Tue, Nov 15, 2022 at 11:49:12AM IST, Alexei Starovoitov wrote:
> On Tue, Nov 15, 2022 at 12:45:38AM +0530, Kumar Kartikeya Dwivedi wrote:
> > Introduce type safe memory allocator bpf_obj_new for BPF programs. The
> > kernel side kfunc is named bpf_obj_new_impl, as passing hidden arguments
> > to kfuncs still requires having them in prototype, unlike BPF helpers
> > which always take 5 arguments and have them checked using bpf_func_proto
> > in verifier, ignoring unset argument types.
> >
> > Introduce __ign suffix to ignore a specific kfunc argument during type
> > checks, then use this to introduce support for passing type metadata to
> > the bpf_obj_new_impl kfunc.
> >
> > The user passes BTF ID of the type it wants to allocates in program BTF,
> > the verifier then rewrites the first argument as the size of this type,
> > after performing some sanity checks (to ensure it exists and it is a
> > struct type).
> >
> > The second argument is also fixed up and passed by the verifier. This is
> > the btf_struct_meta for the type being allocated. It would be needed
> > mostly for the offset array which is required for zero initializing
> > special fields while leaving the rest of storage in unitialized state.
> >
> > It would also be needed in the next patch to perform proper destruction
> > of the object's special fields.
> >
> > Under the hood, bpf_obj_new will call bpf_mem_alloc and bpf_mem_free,
> > using the any context BPF memory allocator introduced recently. To this
> > end, a global instance of the BPF memory allocator is initialized on
> > boot to be used for this purpose. This 'bpf_global_ma' serves all
> > allocations for bpf_obj_new. In the future, bpf_obj_new variants will
> > allow specifying a custom allocator.
> >
> > Note that now that bpf_obj_new can be used to allocate objects that can
> > be linked to BPF linked list (when future linked list helpers are
> > available), we need to also free the elements using bpf_mem_free.
> > However, since the draining of elements is done outside the
> > bpf_spin_lock, we need to do migrate_disable around the call since
> > bpf_list_head_free can be called from map free path where migration is
> > enabled. Otherwise, when called from BPF programs migration is already
> > disabled.
> >
> > A convenience macro is included in the bpf_experimental.h header to hide
> > over the ugly details of the implementation, leading to user code
> > looking similar to a language level extension which allocates and
> > constructs fields of a user type.
> >
> > struct bar {
> > struct bpf_list_node node;
> > };
> >
> > struct foo {
> > struct bpf_spin_lock lock;
> > struct bpf_list_head head __contains(bar, node);
> > };
> >
> > void prog(void) {
> > struct foo *f;
> >
> > f = bpf_obj_new(typeof(*f));
> > if (!f)
> > return;
> > ...
> > }
> >
> > A key piece of this story is still missing, i.e. the free function,
> > which will come in the next patch.
> >
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > ---
> > include/linux/bpf.h | 21 ++--
> > include/linux/bpf_verifier.h | 2 +
> > kernel/bpf/core.c | 16 +++
> > kernel/bpf/helpers.c | 47 ++++++--
> > kernel/bpf/verifier.c | 107 ++++++++++++++++--
> > .../testing/selftests/bpf/bpf_experimental.h | 25 ++++
> > 6 files changed, 195 insertions(+), 23 deletions(-)
> > create mode 100644 tools/testing/selftests/bpf/bpf_experimental.h
> >
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index 62a16b699e71..4635e31bd6fc 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -54,6 +54,8 @@ struct cgroup;
> > extern struct idr btf_idr;
> > extern spinlock_t btf_idr_lock;
> > extern struct kobject *btf_kobj;
> > +extern struct bpf_mem_alloc bpf_global_ma;
> > +extern bool bpf_global_ma_set;
> >
> > typedef u64 (*bpf_callback_t)(u64, u64, u64, u64, u64);
> > typedef int (*bpf_iter_init_seq_priv_t)(void *private_data,
> > @@ -333,16 +335,19 @@ static inline bool btf_record_has_field(const struct btf_record *rec, enum btf_f
> > return rec->field_mask & type;
> > }
> >
> > -static inline void check_and_init_map_value(struct bpf_map *map, void *dst)
> > +static inline void bpf_obj_init(const struct btf_field_offs *foffs, void *obj)
> > {
> > - if (!IS_ERR_OR_NULL(map->record)) {
> > - struct btf_field *fields = map->record->fields;
> > - u32 cnt = map->record->cnt;
> > - int i;
> > + int i;
> >
> > - for (i = 0; i < cnt; i++)
> > - memset(dst + fields[i].offset, 0, btf_field_type_size(fields[i].type));
> > - }
> > + if (!foffs)
> > + return;
> > + for (i = 0; i < foffs->cnt; i++)
> > + memset(obj + foffs->field_off[i], 0, foffs->field_sz[i]);
> > +}
> > +
> > +static inline void check_and_init_map_value(struct bpf_map *map, void *dst)
> > +{
> > + bpf_obj_init(map->field_offs, dst);
> > }
> >
> > /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and
> > diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> > index 887fa4d922f6..306fc1d6cc4a 100644
> > --- a/include/linux/bpf_verifier.h
> > +++ b/include/linux/bpf_verifier.h
> > @@ -427,6 +427,8 @@ struct bpf_insn_aux_data {
> > */
> > struct bpf_loop_inline_state loop_inline_state;
> > };
> > + u64 obj_new_size; /* remember the size of type passed to bpf_obj_new to rewrite R1 */
> > + struct btf_struct_meta *kptr_struct_meta;
> > u64 map_key_state; /* constant (32 bit) key tracking for maps */
> > int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
> > u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
> > diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> > index 9c16338bcbe8..2e57fc839a5c 100644
> > --- a/kernel/bpf/core.c
> > +++ b/kernel/bpf/core.c
> > @@ -34,6 +34,7 @@
> > #include <linux/log2.h>
> > #include <linux/bpf_verifier.h>
> > #include <linux/nodemask.h>
> > +#include <linux/bpf_mem_alloc.h>
> >
> > #include <asm/barrier.h>
> > #include <asm/unaligned.h>
> > @@ -60,6 +61,9 @@
> > #define CTX regs[BPF_REG_CTX]
> > #define IMM insn->imm
> >
> > +struct bpf_mem_alloc bpf_global_ma;
> > +bool bpf_global_ma_set;
> > +
> > /* No hurry in this branch
> > *
> > * Exported for the bpf jit load helper.
> > @@ -2746,6 +2750,18 @@ int __weak bpf_arch_text_invalidate(void *dst, size_t len)
> > return -ENOTSUPP;
> > }
> >
> > +#ifdef CONFIG_BPF_SYSCALL
> > +static int __init bpf_global_ma_init(void)
> > +{
> > + int ret;
> > +
> > + ret = bpf_mem_alloc_init(&bpf_global_ma, 0, false);
> > + bpf_global_ma_set = !ret;
> > + return ret;
> > +}
> > +late_initcall(bpf_global_ma_init);
> > +#endif
> > +
> > DEFINE_STATIC_KEY_FALSE(bpf_stats_enabled_key);
> > EXPORT_SYMBOL(bpf_stats_enabled_key);
> >
> > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> > index 5bc0b9f0f306..c4f1c22cc44c 100644
> > --- a/kernel/bpf/helpers.c
> > +++ b/kernel/bpf/helpers.c
> > @@ -19,6 +19,7 @@
> > #include <linux/proc_ns.h>
> > #include <linux/security.h>
> > #include <linux/btf_ids.h>
> > +#include <linux/bpf_mem_alloc.h>
> >
> > #include "../../lib/kstrtox.h"
> >
> > @@ -1735,25 +1736,57 @@ void bpf_list_head_free(const struct btf_field *field, void *list_head,
> >
> > obj -= field->list_head.node_offset;
> > head = head->next;
> > - /* TODO: Rework later */
> > - kfree(obj);
> > + /* The contained type can also have resources, including a
> > + * bpf_list_head which needs to be freed.
> > + */
> > + bpf_obj_free_fields(field->list_head.value_rec, obj);
> > + /* bpf_mem_free requires migrate_disable(), since we can be
> > + * called from map free path as well apart from BPF program (as
> > + * part of map ops doing bpf_obj_free_fields).
> > + */
> > + migrate_disable();
> > + bpf_mem_free(&bpf_global_ma, obj);
> > + migrate_enable();
> > }
> > }
> >
> > -BTF_SET8_START(tracing_btf_ids)
> > +__diag_push();
> > +__diag_ignore_all("-Wmissing-prototypes",
> > + "Global functions as their definitions will be in vmlinux BTF");
> > +
> > +void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign)
> > +{
> > + struct btf_struct_meta *meta = meta__ign;
> > + u64 size = local_type_id__k;
> > + void *p;
> > +
> > + if (unlikely(!bpf_global_ma_set))
> > + return NULL;
> > + p = bpf_mem_alloc(&bpf_global_ma, size);
> > + if (!p)
> > + return NULL;
> > + if (meta)
> > + bpf_obj_init(meta->field_offs, p);
> > + return p;
> > +}
> > +
> > +__diag_pop();
> > +
> > +BTF_SET8_START(generic_btf_ids)
> > #ifdef CONFIG_KEXEC_CORE
> > BTF_ID_FLAGS(func, crash_kexec, KF_DESTRUCTIVE)
> > #endif
> > -BTF_SET8_END(tracing_btf_ids)
> > +BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL)
> > +BTF_SET8_END(generic_btf_ids)
> >
> > -static const struct btf_kfunc_id_set tracing_kfunc_set = {
> > +static const struct btf_kfunc_id_set generic_kfunc_set = {
> > .owner = THIS_MODULE,
> > - .set = &tracing_btf_ids,
> > + .set = &generic_btf_ids,
> > };
> >
> > static int __init kfunc_init(void)
> > {
> > - return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &tracing_kfunc_set);
> > + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set);
> > }
> >
> > late_initcall(kfunc_init);
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index a4a1424b19a5..c7f5d83783db 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -7948,6 +7948,11 @@ static bool is_kfunc_arg_sfx_constant(const struct btf *btf, const struct btf_pa
> > return __kfunc_param_match_suffix(btf, arg, "__k");
> > }
> >
> > +static bool is_kfunc_arg_sfx_ignore(const struct btf *btf, const struct btf_param *arg)
> > +{
> > + return __kfunc_param_match_suffix(btf, arg, "__ign");
> > +}
> > +
> > static bool is_kfunc_arg_ret_buf_size(const struct btf *btf,
> > const struct btf_param *arg,
> > const struct bpf_reg_state *reg,
> > @@ -8216,6 +8221,10 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
> > int kf_arg_type;
> >
> > t = btf_type_skip_modifiers(btf, args[i].type, NULL);
> > +
> > + if (is_kfunc_arg_sfx_ignore(btf, &args[i]))
> > + continue;
> > +
> > if (btf_type_is_scalar(t)) {
> > if (reg->type != SCALAR_VALUE) {
> > verbose(env, "R%d is not a scalar\n", regno);
> > @@ -8395,6 +8404,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
> > return 0;
> > }
> >
> > +enum special_kfunc_type {
> > + KF_bpf_obj_new_impl,
> > +};
> > +
> > +BTF_SET_START(special_kfunc_set)
> > +BTF_ID(func, bpf_obj_new_impl)
> > +BTF_SET_END(special_kfunc_set)
> > +
> > +BTF_ID_LIST(special_kfunc_list)
> > +BTF_ID(func, bpf_obj_new_impl)
> > +
> > static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > int *insn_idx_p)
> > {
> > @@ -8469,17 +8489,64 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> > t = btf_type_skip_modifiers(desc_btf, func_proto->type, NULL);
> >
> > if (is_kfunc_acquire(&meta) && !btf_type_is_struct_ptr(meta.btf, t)) {
> > - verbose(env, "acquire kernel function does not return PTR_TO_BTF_ID\n");
> > - return -EINVAL;
> > + /* Only exception is bpf_obj_new_impl */
> > + if (meta.btf != btf_vmlinux || meta.func_id != special_kfunc_list[KF_bpf_obj_new_impl]) {
> > + verbose(env, "acquire kernel function does not return PTR_TO_BTF_ID\n");
> > + return -EINVAL;
> > + }
> > }
> >
> > if (btf_type_is_scalar(t)) {
> > mark_reg_unknown(env, regs, BPF_REG_0);
> > mark_btf_func_reg_size(env, BPF_REG_0, t->size);
> > } else if (btf_type_is_ptr(t)) {
> > - ptr_type = btf_type_skip_modifiers(desc_btf, t->type,
> > - &ptr_type_id);
> > - if (!btf_type_is_struct(ptr_type)) {
> > + ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id);
> > +
> > + if (meta.btf == btf_vmlinux && btf_id_set_contains(&special_kfunc_set, meta.func_id)) {
> > + if (!btf_type_is_void(ptr_type)) {
> > + verbose(env, "kernel function %s must have void * return type\n",
> > + meta.func_name);
> > + return -EINVAL;
> > + }
>
> Here you're checking that void *bpf_obj_new_impl and obj_drop actually were
> declared with 'void *' return ?
> and wasting run-time cycle to check that??
> Please don't.
>
> Even if we miss that during code review there is no safety issue.
>
Right, I think the later linked list patch actually drops this hunk anyway. We
fail if any of the other cases are unhandled anyway.
next prev parent reply other threads:[~2022-11-15 16:53 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-14 19:15 [PATCH bpf-next v7 00/26] Allocated objects, BPF linked lists Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 01/26] bpf: Remove local kptr references in documentation Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 02/26] bpf: Remove BPF_MAP_OFF_ARR_MAX Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 03/26] bpf: Fix copy_map_value, zero_map_value Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 04/26] bpf: Support bpf_list_head in map values Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 05/26] bpf: Rename RET_PTR_TO_ALLOC_MEM Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 06/26] bpf: Rename MEM_ALLOC to MEM_RINGBUF Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 07/26] bpf: Refactor btf_struct_access Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 08/26] bpf: Introduce allocated objects support Kumar Kartikeya Dwivedi
2022-11-15 5:48 ` Alexei Starovoitov
2022-11-15 16:47 ` Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 09/26] bpf: Recognize lock and list fields in allocated objects Kumar Kartikeya Dwivedi
2022-11-15 5:50 ` Alexei Starovoitov
2022-11-15 16:48 ` Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 10/26] bpf: Verify ownership relationships for user BTF types Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 11/26] bpf: Allow locking bpf_spin_lock in allocated objects Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 12/26] bpf: Allow locking bpf_spin_lock global variables Kumar Kartikeya Dwivedi
2022-11-15 17:33 ` Dave Marchevsky
2022-11-15 19:37 ` Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 13/26] bpf: Allow locking bpf_spin_lock in inner map values Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 14/26] bpf: Rewrite kfunc argument handling Kumar Kartikeya Dwivedi
2022-11-15 6:10 ` Alexei Starovoitov
2022-11-15 16:51 ` Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 15/26] bpf: Drop kfunc bits from btf_check_func_arg_match Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 16/26] bpf: Support constant scalar arguments for kfuncs Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 17/26] bpf: Introduce bpf_obj_new Kumar Kartikeya Dwivedi
2022-11-15 6:19 ` Alexei Starovoitov
2022-11-15 16:53 ` Kumar Kartikeya Dwivedi [this message]
2022-11-14 19:15 ` [PATCH bpf-next v7 18/26] bpf: Introduce bpf_obj_drop Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 19/26] bpf: Permit NULL checking pointer with non-zero fixed offset Kumar Kartikeya Dwivedi
2022-11-15 6:23 ` Alexei Starovoitov
2022-11-15 16:57 ` Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 20/26] bpf: Introduce single ownership BPF linked list API Kumar Kartikeya Dwivedi
2022-11-15 6:26 ` Alexei Starovoitov
2022-11-15 16:59 ` Kumar Kartikeya Dwivedi
2022-11-15 18:26 ` Alexei Starovoitov
2022-11-15 19:36 ` Kumar Kartikeya Dwivedi
2022-11-15 19:45 ` Alexei Starovoitov
2022-11-14 19:15 ` [PATCH bpf-next v7 21/26] bpf: Add 'release on unlock' logic for bpf_list_push_{front,back} Kumar Kartikeya Dwivedi
2022-11-15 16:52 ` Dave Marchevsky
2022-11-15 17:01 ` Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 22/26] selftests/bpf: Add __contains macro to bpf_experimental.h Kumar Kartikeya Dwivedi
2022-11-15 16:40 ` Dave Marchevsky
2022-11-14 19:15 ` [PATCH bpf-next v7 23/26] selftests/bpf: Update spinlock selftest Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 24/26] selftests/bpf: Add failure test cases for spin lock pairing Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 25/26] selftests/bpf: Add BPF linked list API tests Kumar Kartikeya Dwivedi
2022-11-14 19:15 ` [PATCH bpf-next v7 26/26] selftests/bpf: Add BTF sanity tests Kumar Kartikeya Dwivedi
2022-11-15 6:00 ` [PATCH bpf-next v7 00/26] Allocated objects, BPF linked lists patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221115165341.7y4hmnkfexjbo6oe@apollo \
--to=memxor@gmail.com \
--cc=alexei.starovoitov@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davemarchevsky@meta.com \
--cc=martin.lau@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox