From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
To: Martin KaFai Lau <martin.lau@linux.dev>
Cc: Amery Hung <amery.hung@bytedance.com>, <bpf@vger.kernel.org>,
<magnus.karlsson@intel.com>, <sreedevi.joshi@intel.com>,
<ast@kernel.org>
Subject: Re: [External] Storing sk_buffs as kptrs in map
Date: Fri, 6 Dec 2024 17:24:53 +0100 [thread overview]
Message-ID: <Z1MlVa3OXQJw0VXm@boxer> (raw)
In-Reply-To: <ecd47c2c-7b34-4649-ad97-3988c7644317@linux.dev>
On Wed, Dec 04, 2024 at 03:24:33PM -0800, Martin KaFai Lau wrote:
> On 12/3/24 12:46 PM, Maciej Fijalkowski wrote:
> > ; bpf_skb_release((struct __sk_buff *)tmp); @ bpf_bpf.c:161
> > 36: (bf) r1 = r6 ; R1_w=ptr_sk_buff(ref_obj_id=3) R6=ptr_sk_buff(ref_obj_id=3) refs=3
> > 37: (85) call bpf_skb_release#102037
> > arg#0 expected pointer to ctx, but got ptr_
>
> ic. The bpf_skb_release() is hitting the KF_ARG_PTR_TO_CTX again.
>
> > processed 34 insns (limit 1000000) max_states_per_insn 0 total_states 3 peak_states 3 mark_read 1
> > -- END PROG LOAD LOG --
> >
> >
> > Still the same problem. Also even it would work it was not very convenient
> > to cast these types back and forth...I then tried to store __sk_buff as
> > kptr but I ended up with:
> >
> > "map 'skb_map' has to have BTF in order to use bpf_kptr_xchg"
> >
> > which got me lost:) I have a solution though which I'd like to discuss.
> >
> > >
> > > Please share the patch and the test case. It will be easier for others to help.
> >
> > I have come up with rather simple way of achieving what I desired when
> > starting this thread, how about something like this:
> >
> > From 0df7760330cccfe71235b56018d0a33d4a3b9863 Mon Sep 17 00:00:00 2001
> > From: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> > Date: Tue, 3 Dec 2024 21:00:44 +0100
> > Subject: [PATCH RFC bpf-next] bpf: add __ctx_ref kfunc argument suffix
> >
> > The use case is when user wants to use sk_buff pointers as kptrs against
> > program types that take in __sk_buff struct as context.
> >
> > A pair of kfuncs for acquiring and releasing skb would look as follows:
> >
> > __bpf_kfunc struct sk_buff *bpf_skb_acquire(struct sk_buff *skb__ctx_ref)
> > __bpf_kfunc void bpf_skb_release(struct sk_buff *skb__ctx_ref)
> >
> > Signed-off-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> > ---
> > kernel/bpf/verifier.c | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 60938b136365..b16a39d28f8a 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -11303,6 +11303,11 @@ static bool is_kfunc_arg_const_str(const struct btf *btf, const struct btf_param
> > return btf_param_match_suffix(btf, arg, "__str");
> > }
> > +static bool is_kfunc_arg_ctx_ref(const struct btf *btf, const struct btf_param *arg)
> > +{
> > + return btf_param_match_suffix(btf, arg, "__ctx_ref");
>
> imo, new tagging is not needed. It does not give new information to
> the ptr type. I still think the verifier can be taught to handle
> it better.
>
> I took a closer look. I think the issue is btf_is_prog_ctx_type() selectively
> treating some kfunc arg type as the uapi prog ctx instead of honoring what
> has been written in the kernel source code.
>
> The projection_of test was first added in
> commit 2f4643934670 ("bpf: Support "sk_buff" and "xdp_buff" as valid kfunc arg types").
> It was originally added to allow the kfunc to accept reg->type == PTR_TO_CTX
> as its "struct sk_buff/xdp_buff *" argument.
>
> After commit cce4c40b9606 ("bpf: treewide: Align kfunc signatures to prog point-of-view"),
> the need of projection_of in btf_is_prog_ctx_type() should be gone.
> However, this projection_of check has an unintentional side effect
> for the other btf_is_prog_ctx_type() callers. It treats subprog
> (in the bpf prog code) taking the "struct sk_buff *skb" arg as the uapi
> prog ctx also. I don't think the bpf prog should do this though. I have a
> "call_dynptr_skb" subprog to show this.
Nice that's what I wanted to say from the very beginning but didn't have a
good justification in terms of getting rid of projection_of() call :)
>
> For the skb acquire/release kfunc, I think it is better to begin with
> the "struct sk_buff *" as its arg type and return type
> instead of "struct __sk_buff *" because it is the __kptr type stored
> in the map. The kptr_xchg will also return the PTR_TO_BTF_ID.
Yep that's also what I stumbled upon, that __sk_buff as kptr will not
work.
> It will need another cast call for acquire, like
> "bpf_skb_acauire(bpf_cast_to_kern_ctx(ctx))" but this should be fine.
> The "struct sk_buff __kptr *skb" stored in the map cannot
> be passed to the bpf_dynptr_from_skb() also. It shouldn't be
> allowed because bpf_dynptr_from_skb will allow skb write
> in the tc bpf prog. The same goes for other tc bpf helpers which
> takes ARG_PTR_TO_CTX.
>
> I think we can remove the projection_of call from the
> bpf_is_prog_ctx_type() such that it honors the exact argument
> type written in the kernel source code. Add this particular projection_of
> check (renamed to bpf_is_kern_ctx in the diff) to the other callers for
> backward compat such that the caller can selectively translate
> the argument of a subprog to the corresponding prog ctx type.
>
> Lightly tested only:
I tried the kernel diff on my side and it addressed my needs. Will you
send a patch?
>
> diff --git i/kernel/bpf/btf.c w/kernel/bpf/btf.c
> index e7a59e6462a9..2d39f91617fb 100644
> --- i/kernel/bpf/btf.c
> +++ w/kernel/bpf/btf.c
> @@ -5914,6 +5914,26 @@ bool btf_is_projection_of(const char *pname, const char *tname)
> return false;
> }
> +static bool btf_is_kern_ctx(const struct btf *btf,
> + const struct btf_type *t,
> + enum bpf_prog_type prog_type)
> +{
> + const struct btf_type *ctx_type;
> + const char *tname, *ctx_tname;
> +
> + t = btf_type_skip_modifiers(btf, t->type, NULL);
> + if (!btf_type_is_struct(t))
> + return false;
> +
> + tname = btf_name_by_offset(btf, t->name_off);
> + if (!tname)
> + return false;
> +
> + ctx_type = find_canonical_prog_ctx_type(prog_type);
> + ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);
> + return btf_is_projection_of(ctx_tname, tname);
We're sort of doubling the work that btf_is_prog_ctx_type() is doing also,
maybe add a flag to btf_is_prog_ctx_type() that will allow us to skip
btf_is_projection_of() call when needed? e.g. in get_kfunc_ptr_arg_type().
Thanks for helping here and I look forward for patch submission:)
> +}
> +
> bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
> const struct btf_type *t, enum bpf_prog_type prog_type,
> int arg)
> @@ -5976,8 +5996,6 @@ bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
> * int socket_filter_bpf_prog(struct __sk_buff *skb)
> * { // no fields of skb are ever used }
> */
> - if (btf_is_projection_of(ctx_tname, tname))
> - return true;
> if (strcmp(ctx_tname, tname)) {
> /* bpf_user_pt_regs_t is a typedef, so resolve it to
> * underlying struct and check name again
> @@ -6140,7 +6158,8 @@ static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,
> enum bpf_prog_type prog_type,
> int arg)
> {
> - if (!btf_is_prog_ctx_type(log, btf, t, prog_type, arg))
> + if (!btf_is_prog_ctx_type(log, btf, t, prog_type, arg) &&
> + !btf_is_kern_ctx(btf, t, prog_type))
> return -ENOENT;
> return find_kern_ctx_type_id(prog_type);
> }
> @@ -7505,7 +7524,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
> if (!btf_type_is_ptr(t))
> goto skip_pointer;
> - if ((tags & ARG_TAG_CTX) || btf_is_prog_ctx_type(log, btf, t, prog_type, i)) {
> + if ((tags & ARG_TAG_CTX) || btf_is_prog_ctx_type(log, btf, t, prog_type, i) ||
> + btf_is_kern_ctx(btf, t, prog_type)) {
> if (tags & ~ARG_TAG_CTX) {
> bpf_log(log, "arg#%d has invalid combination of tags\n", i);
> return -EINVAL;
>
> diff --git a/tools/testing/selftests/bpf/progs/skb_acquire.c b/tools/testing/selftests/bpf/progs/skb_acquire.c
> new file mode 100644
> index 000000000000..65d62fd97905
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/skb_acquire.c
> @@ -0,0 +1,59 @@
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_misc.h"
> +#include "bpf_kfuncs.h"
> +#include "bpf_tracing_net.h"
> +
> +struct sk_buff *dummy_skb;
> +
> +struct sk_buff *bpf_skb_acquire(struct sk_buff *skb) __ksym;
> +void bpf_skb_release(struct sk_buff *skb) __ksym;
> +void *bpf_cast_to_kern_ctx(void *) __ksym;
> +
> +struct map_value {
> + int a;
> + struct sk_buff __kptr *skb;
> +};
> +
> +struct {
> + __uint(type, BPF_MAP_TYPE_ARRAY);
> + __type(key, int);
> + __type(value, struct map_value);
> + __uint(max_entries, 16);
> +} skb_map SEC(".maps");
> +
> +__noinline int call_dynptr_skb(struct sk_buff *skb)
> +{
> + struct bpf_dynptr ptr;
> +
> + bpf_dynptr_from_skb((struct __sk_buff *)skb, 0, &ptr);
> +
> + return 0;
> +}
> +
> +__success
> +SEC("tc")
> +int bpf_tc_egress(struct __sk_buff *ctx)
> +{
> + struct sk_buff *skb;
> + struct map_value *map_entry;
> + u32 zero = 0;
> +
> + call_dynptr_skb((struct sk_buff *)ctx);
> +
> + map_entry = bpf_map_lookup_elem(&skb_map, &zero);
> + if (!map_entry)
> + return TC_ACT_SHOT;
> +
> + skb = bpf_skb_acquire(bpf_cast_to_kern_ctx(ctx));
> + if (!skb)
> + return TC_ACT_SHOT;
> +
> + skb = bpf_kptr_xchg(&map_entry->skb, skb);
> + if (skb)
> + bpf_skb_release(skb);
> +
> + return TC_ACT_OK;
> +}
> +
> +char LICENSE[] SEC("license") = "GPL";
next prev parent reply other threads:[~2024-12-06 16:25 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-26 17:05 Storing sk_buffs as kptrs in map Maciej Fijalkowski
2024-11-26 19:56 ` [External] " Amery Hung
2024-11-26 20:47 ` Martin KaFai Lau
2024-11-27 19:07 ` Maciej Fijalkowski
2024-11-27 20:54 ` Martin KaFai Lau
2024-12-03 20:46 ` Maciej Fijalkowski
2024-12-04 23:24 ` Martin KaFai Lau
2024-12-06 16:24 ` Maciej Fijalkowski [this message]
2024-12-07 0:36 ` Martin KaFai Lau
2024-12-09 13:17 ` Maciej Fijalkowski
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=Z1MlVa3OXQJw0VXm@boxer \
--to=maciej.fijalkowski@intel.com \
--cc=amery.hung@bytedance.com \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=magnus.karlsson@intel.com \
--cc=martin.lau@linux.dev \
--cc=sreedevi.joshi@intel.com \
/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