bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Matt Bobrowski <mattbobrowski@google.com>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: "Toke Høiland-Jørgensen" <toke@redhat.com>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"Alexei Starovoitov" <ast@kernel.org>,
	"Martin KaFai Lau" <kafai@fb.com>,
	"Song Liu" <songliubraving@fb.com>, "Yonghong Song" <yhs@fb.com>,
	"Marek Majkowski" <marek@cloudflare.com>,
	"Lorenz Bauer" <lmb@cloudflare.com>,
	"Alan Maguire" <alan.maguire@oracle.com>,
	"Jesper Dangaard Brouer" <brouer@redhat.com>,
	"David Miller" <davem@davemloft.net>,
	netdev@vger.kernel.org, bpf@vger.kernel.org
Subject: Re: bpf indirect calls
Date: Wed, 27 Sep 2023 13:27:15 +0000	[thread overview]
Message-ID: <ZRQtsyYM810Oh4px@google.com> (raw)
In-Reply-To: <20191019200939.kiwuaj7c4bg25vqs@ast-mbp>

On Sat, Oct 19, 2019 at 01:09:42PM -0700, Alexei Starovoitov wrote:
> On Wed, Oct 16, 2019 at 03:51:52PM +0200, Toke Høiland-Jørgensen wrote:
> > Alexei Starovoitov <alexei.starovoitov@gmail.com> writes:
> > 
> > > On Mon, Oct 14, 2019 at 02:35:45PM +0200, Toke Høiland-Jørgensen wrote:
> > >> Alexei Starovoitov <alexei.starovoitov@gmail.com> writes:
> > >> 
> > >> > On Wed, Oct 09, 2019 at 10:03:43AM +0200, Toke Høiland-Jørgensen wrote:
> > >> >> Alexei Starovoitov <alexei.starovoitov@gmail.com> writes:
> > >> >> 
> > >> >> > Please implement proper indirect calls and jumps.
> > >> >> 
> > >> >> I am still not convinced this will actually solve our problem; but OK, I
> > >> >> can give it a shot.
> > >> >
> > >> > If you're not convinced let's talk about it first.
> > >> >
> > >> > Indirect calls is a building block for debugpoints.
> > >> > Let's not call them tracepoints, because Linus banned any discusion
> > >> > that includes that name.
> > >> > The debugpoints is a way for BPF program to insert points in its
> > >> > code to let external facility to do tracing and debugging.
> > >> >
> > >> > void (*debugpoint1)(struct xdp_buff *, int code);
> > >> > void (*debugpoint2)(struct xdp_buff *);
> > >> > void (*debugpoint3)(int len);
> > >> 
> > >> So how would these work? Similar to global variables (i.e., the loader
> > >> creates a single-entry PROG_ARRAY map for each one)? Presumably with
> > >> some BTF to validate the argument types?
> > >> 
> > >> So what would it take to actually support this? It doesn't quite sound
> > >> trivial to add?
> > >
> > > Depends on definition of 'trivial' :)
> > 
> > Well, I don't know... :)
> > 
> > > The kernel has a luxury of waiting until clean solution is implemented
> > > instead of resorting to hacks.
> > 
> > It would be helpful if you could give an opinion on what specific
> > features are missing in the kernel to support these indirect calls. A
> > few high-level sentences is fine (e.g., "the verifier needs to be able
> > to do X, and llvm/libbpf needs to have support for Y")... I'm trying to
> > gauge whether this is something it would even make sense for me to poke
> > into, or if I'm better off waiting for someone who actually knows what
> > they are doing to work on this :)
> 
> I have to reveal a secret first...
> llvm supports indirect calls since 2017 ;)
> 
> It can compile the following:
> struct trace_kfree_skb {
> 	struct sk_buff *skb;
> 	void *location;
> };
> 
> typedef void (*fn)(struct sk_buff *skb);
> static fn func;
> 
> SEC("tp_btf/kfree_skb")
> int trace_kfree_skb(struct trace_kfree_skb *ctx)
> {
> 	struct sk_buff *skb = ctx->skb;
> 	fn f = *(volatile fn *)&func;
> 
> 	if (f)
> 		f(skb);
> 	return 0;
> }
> 
> into proper BPF assembly:
> ; 	struct sk_buff *skb = ctx->skb;
>        0:	79 11 00 00 00 00 00 00	r1 = *(u64 *)(r1 + 0)
> ; 	fn f = *(volatile fn *)&func;
>        1:	18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00	r2 = 0 ll
>        3:	79 22 00 00 00 00 00 00	r2 = *(u64 *)(r2 + 0)
> ; 	if (f)
>        4:	15 02 01 00 00 00 00 00	if r2 == 0 goto +1 <LBB0_2>
> ; 		f(skb);
>        5:	8d 00 00 00 02 00 00 00	callx 2
> 0000000000000030 LBB0_2:
> ; 	return 0;
>        6:	b7 00 00 00 00 00 00 00	r0 = 0
>        7:	95 00 00 00 00 00 00 00	exit
> 
> Indirect call is encoded as JMP|CALL|X
> Normal call is JMP|CALL|K
> 
> What's left to do is to teach the verifier to parse BTF of global data.
> Then teach it to recognize that r2 at insn 1 is PTR_TO_BTF_ID
> where btf_id is DATASEC '.bss'
> Then load r2+0 is also PTR_TO_BTF_ID where btf_id is VAR 'func'.
> New bool flag to reg_state is needed to tell whether if(rX==NULL) check
> was completed.
> Then at insn 5 the verifier will see that R2 is PTR_TO_BTF_ID and !NULL
> and it's a pointer to a function.
> Depending on function prototype the verifier would need to check that
> R1's type match to arg1 of func proto.
> For simplicity we don't need to deal with pointers to stack,
> pointers to map, etc. Only PTR_TO_BTF_ID where btf_id is a kernel
> data structure or scalar is enough to get a lot of mileage out of
> this indirect call feature.
> That's mostly it.
> 
> Few other safety checks would be needed to make sure that writes
> into 'r2+0' are also of correct type.
> We also need partial map_update bpf_sys command to populate
> function pointer with another bpf program that has matching
> function proto.
> 
> I think it's not trivial verifier work, but not hard either.
> I'm happy to do it as soon as I find time to work on it.

Alexei,

Sorry to resurrect this incredibly old thread, but I was wondering
whether BPF indirect calls are supported in the latest kernel
versions?

I was performing some experiments within a BPF program today which
leveraged indirect calls, but I was continuously running into BPF
verifier errors, specifically errors related to an "unknown opcode 8d"
being used within the BPF program. It turns out that the encoded
opcode "8d" decodes to "BPF_JMP | BPF_CALL | BPF_X", which apparently
is forbidden based on the bpf_opcode_in_insntable() check, but given
this thread I wasn't exactly sure what the status was on indirect
calls support?

Note that the example BPF program that I was attempting to load on a
6.4 kernel was as follows:
```
struct {
  __uint(type, BPF_MAP_TYPE_ARRAY);
  __type(key, u32);
  __type(value, u64);
  __uint(max_entries, 32);
} map SEC(".maps");

static void testing(void) {
  bpf_printk("testing");
}

struct iter_ctx {
  void (*f) (void);
};
static u64 iter_callback(struct bpf_map *map, u32 *key,
                         u64 *value, struct iter_ctx *ctx) {
  if (ctx->f) {
    ctx->f();
  }
  return 0;
}

SEC("lsm.s/file_open")
int BPF_PROG(file_open, struct file *file)
{
  struct iter_ctx iter_ctx = {
    .f = testing,
  };
  bpf_for_each_map_elem(&map, iter_callback, &iter_ctx, 0);
  return 0;
}
```

This BPF program produced the following disassembly:
```
0000000000000000 <testing>:
       0:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll
       2:       b7 02 00 00 08 00 00 00 r2 = 0x8
       3:       85 00 00 00 06 00 00 00 call 0x6
       4:       95 00 00 00 00 00 00 00 exit

0000000000000028 <iter_callback>:
       5:       79 41 00 00 00 00 00 00 r1 = *(u64 *)(r4 + 0x0)
       6:       15 01 01 00 00 00 00 00 if r1 == 0x0 goto +0x1 <LBB2_2>
       7:       8d 00 00 00 01 00 00 00 callx r1

0000000000000040 <LBB2_2>:
       8:       b7 00 00 00 00 00 00 00 r0 = 0x0
       9:       95 00 00 00 00 00 00 00 exit

Disassembly of section lsm.s/file_open:

0000000000000000 <file_open>:
       0:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll
       2:       7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 0x8) = r1
       3:       bf a3 00 00 00 00 00 00 r3 = r10
       4:       07 03 00 00 f8 ff ff ff r3 += -0x8
       5:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll
       7:       18 02 00 00 28 00 00 00 00 00 00 00 00 00 00 00 r2 = 0x28 ll
       9:       b7 04 00 00 00 00 00 00 r4 = 0x0
      10:       85 00 00 00 a4 00 00 00 call 0xa4
      11:       b7 00 00 00 00 00 00 00 r0 = 0x0
      12:       95 00 00 00 00 00 00 00 exit
```

What's interesting though is that with a somewhat similar BPF program
which made use of indirect calls, the BPF verifier generated no errors:

```
static __attribute__((noinline)) void testing(void) {
  bpf_printk("testing");
}

SEC("lsm.s/file_open")
int BPF_PROG(file_open, struct file *file)
{
  void (*f) (void) = testing;
  f();
  return 0;
}
```

This BPF program produced the following disassembly:
```
0000000000000000 <testing>:
       0:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll
       2:       b7 02 00 00 08 00 00 00 r2 = 0x8
       3:       85 00 00 00 06 00 00 00 call 0x6
       4:       95 00 00 00 00 00 00 00 exit

Disassembly of section lsm.s/file_open:

0000000000000000 <file_open>:
       0:       85 10 00 00 ff ff ff ff call -0x1
       1:       b7 00 00 00 00 00 00 00 r0 = 0x0
       2:       95 00 00 00 00 00 00 00 exit
```

The fundamental difference between the two call instructions if I'm
not mistaken is that one attempts to perform a call using an immediate
value as its source operand, whereas the other attempts to perform a
call using a source register as its source operand. AFAIU, the latter
is not currently permitted by the BPF verifier. Is that right?

/M

  parent reply	other threads:[~2023-09-27 13:27 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-07 17:20 [PATCH bpf-next v3 0/5] xdp: Support multiple programs on a single interface through chain calls Toke Høiland-Jørgensen
2019-10-07 17:20 ` [PATCH bpf-next v3 1/5] bpf: Support chain calling multiple BPF programs after each other Toke Høiland-Jørgensen
2019-10-07 20:42   ` Alexei Starovoitov
2019-10-08  8:07     ` Toke Høiland-Jørgensen
2019-10-09  1:51       ` Alexei Starovoitov
2019-10-09  8:03         ` Toke Høiland-Jørgensen
2019-10-10  4:41           ` Alexei Starovoitov
2019-10-14 12:35             ` Toke Høiland-Jørgensen
2019-10-14 17:08               ` John Fastabend
2019-10-14 18:48                 ` Toke Høiland-Jørgensen
2019-10-15 16:30                   ` Edward Cree
2019-10-15 16:42                     ` Toke Høiland-Jørgensen
2019-10-15 18:33                       ` Edward Cree
2019-10-17 12:11                         ` Toke Høiland-Jørgensen
2019-10-22 17:27                           ` Edward Cree
2019-10-22 18:07                             ` Toke Høiland-Jørgensen
2019-11-12  2:51                               ` static and dynamic linking. Was: [PATCH bpf-next v3 1/5] bpf: Support chain calling multiple BPF Alexei Starovoitov
2019-11-12 16:20                                 ` Toke Høiland-Jørgensen
2019-11-12 19:52                                   ` Alexei Starovoitov
2019-11-12 21:25                                     ` Edward Cree
2019-11-12 23:18                                       ` Alexei Starovoitov
2019-11-13 18:30                                         ` Edward Cree
2019-11-13 18:51                                           ` Andrii Nakryiko
2019-11-15  2:13                                           ` Alexei Starovoitov
2019-11-15 16:56                                             ` John Fastabend
2019-11-12 23:25                                     ` John Fastabend
2019-11-13  0:21                                       ` Alexei Starovoitov
2019-11-13  5:33                                         ` John Fastabend
2019-11-15  1:50                                           ` Alexei Starovoitov
2019-11-15 16:39                                             ` John Fastabend
2019-11-14 15:41                                     ` Toke Høiland-Jørgensen
2019-11-12 16:32                                 ` Edward Cree
2019-11-15 11:48                                 ` Lorenz Bauer
2019-11-15 23:02                                   ` Alexei Starovoitov
2019-11-18 13:29                                     ` Lorenz Bauer
2019-10-21 23:51                         ` [PATCH bpf-next v3 1/5] bpf: Support chain calling multiple BPF programs after each other Edward Cree
2019-10-16  2:28               ` Alexei Starovoitov
2019-10-16  8:27                 ` Jesper Dangaard Brouer
2019-10-16 10:35                   ` Daniel Borkmann
2019-10-16 11:16                     ` Toke Høiland-Jørgensen
2019-10-16 13:51                 ` Toke Høiland-Jørgensen
2019-10-19 20:09                   ` bpf indirect calls Alexei Starovoitov
2019-10-20 10:58                     ` Toke Høiland-Jørgensen
2019-10-25 16:30                       ` Alexei Starovoitov
2019-10-27 12:15                         ` Toke Høiland-Jørgensen
2023-09-27 13:27                     ` Matt Bobrowski [this message]
2023-09-29 21:06                       ` Alexei Starovoitov
2023-10-02 18:50                         ` Barret Rhoden
2023-10-06  9:36                         ` Matt Bobrowski
2023-10-06 18:49                           ` Alexei Starovoitov
2023-10-19 12:28                             ` Matt Bobrowski
2019-10-09 10:19         ` [PATCH bpf-next v3 1/5] bpf: Support chain calling multiple BPF programs after each other Jesper Dangaard Brouer
2019-10-09 17:57           ` Alexei Starovoitov
2019-10-07 17:20 ` [PATCH bpf-next v3 2/5] bpf: Add support for setting chain call sequence for programs Toke Høiland-Jørgensen
2019-10-07 20:38   ` Daniel Borkmann
2019-10-08  8:09     ` Toke Høiland-Jørgensen
2019-10-07 17:20 ` [PATCH bpf-next v3 3/5] tools: Update bpf.h header for program chain calls Toke Høiland-Jørgensen
2019-10-07 17:20 ` [PATCH bpf-next v3 4/5] libbpf: Add syscall wrappers for BPF_PROG_CHAIN_* commands Toke Høiland-Jørgensen
2019-10-07 17:20 ` [PATCH bpf-next v3 5/5] selftests: Add tests for XDP chain calls Toke Høiland-Jørgensen
2019-10-07 18:58 ` [PATCH bpf-next v3 0/5] xdp: Support multiple programs on a single interface through " John Fastabend
2019-10-08  8:42   ` Toke Høiland-Jørgensen

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=ZRQtsyYM810Oh4px@google.com \
    --to=mattbobrowski@google.com \
    --cc=alan.maguire@oracle.com \
    --cc=alexei.starovoitov@gmail.com \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=brouer@redhat.com \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=kafai@fb.com \
    --cc=lmb@cloudflare.com \
    --cc=marek@cloudflare.com \
    --cc=netdev@vger.kernel.org \
    --cc=songliubraving@fb.com \
    --cc=toke@redhat.com \
    --cc=yhs@fb.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;
as well as URLs for NNTP newsgroup(s).