BPF List
 help / color / mirror / Atom feed
From: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>
To: "Daniel Borkmann" <daniel@iogearbox.net>, <memxor@gmail.com>
Cc: <bpf@vger.kernel.org>
Subject: Re: [PATCH bpf-next 2/2] selftests/bpf: Add JIT cBPF-related instruction tests
Date: Thu, 15 Oct 2026 19:08:24 +0000	[thread overview]
Message-ID: <DM5NCJVIY5GR.3KH59CI540I82@gmail.com> (raw)
In-Reply-To: <20261015171917.599025-2-daniel@iogearbox.net>

On Thu Oct 15, 2026 at 5:19 PM UTC, Daniel Borkmann wrote:
> The classic BPF cases of the removed test_bpf module ran their programs
> on a hand-built skb. Run them as socket filters instead via jit_insns_cbpf
> test suite. This covers all previous test_bpf cases as well as a few corner
> cases beyond that.
>
>   # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t jit_insns_cbpf
>   [...]
>   #193/1   jit_insns_cbpf/classic:OK
>   #193/2   jit_insns_cbpf/checker:OK
>   #193/3   jit_insns_cbpf/frags:OK
>   #193/4   jit_insns_cbpf/packets:OK
>   #193/5   jit_insns_cbpf/blinded:OK
>   #193     jit_insns_cbpf:OK
>   Summary: 1/5 PASSED, 0 SKIPPED, 0/0 FAILED
>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---
>  [ Small fyi, the gso related tests I pushed here for net-next
>    https://lore.kernel.org/netdev/20261015151033.591439-1-daniel@iogearbox.net/
>    thus this concludes all of old test_bpf. ]
>
>  .../selftests/bpf/prog_tests/jit_insns_cbpf.c | 2620 +++++++++++++++++
>  1 file changed, 2620 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c b/tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c
> new file mode 100644
> index 000000000000..5471c06605fe
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c
> @@ -0,0 +1,2620 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Isovalent */
> +/*
> + * The classic BPF cases of the former test_bpf module, run as socket
> + * filters: the data cases on an AF_UNIX datagram pair, which takes any
> + * length down to zero and puts the tail of a long datagram into page
> + * frags, the cases reading the link layer or the metadata of a packet on
> + * a veth pair in their own netns. The return of a filter decides over the
> + * packet, zero drops it and any other value truncates it to that length,
> + * so the returns of a program get rewritten: the expected result stays,
> + * any other constant becomes zero and a returned accumulator is compared
> + * at the end, so that a wrong result shows as a dropped packet.
> + */
> +#include <test_progs.h>
> +#include <poll.h>
> +#include <sys/ioctl.h>
> +#include <linux/filter.h>
> +
> +#include "network_helpers.h"
> +#include "sysctl_helpers.h"
> +
> +#define MAX_K		0xffffffff
> +#define SKB_TYPE	PACKET_OTHERHOST	/* the frames go to a foreign MAC */
> +#define SKB_MARK	0x1234aaaa		/* the mark of the sending socket */
> +#define SKB_VLAN_TCI	0xffff
> +#define SKB_VLAN_PRESENT 1
> +#define SKB_DEV_IFINDEX	0			/* that of the receiving veth */
> +#define SKB_DEV_TYPE	1			/* ARPHRD_ETHER, that of a veth */
> +/*
> + * A datagram of four pages splits into three pages of head and one page of
> + * frag data: what exceeds SKB_MAX_ALLOC, four pages less the shared info,
> + * goes into frags, rounded up to a page. The table cases take 4K pages, a
> + * larger page keeps their datagram linear; the sweep goes by the page size.
> + */
> +#define FRAG_OFF	(3 * 4096)		/* the frag data starts here */
> +#define SWEEP_SPAN	16			/* offsets swept either side of it */
> +#define NETNS		"jit_insns_cbpf"
> +
> +static int page_size;
> +static int dgram_max;				/* four pages */
> +
> +enum {
> +	CB_FAIL		= 1,	/* the attach has to fail */
> +	CB_PKT		= 2,	/* needs the link layer or the metadata of a packet */
> +	CB_VLAN		= 4,	/* a tagged frame, the stack pulls the tag out */
> +	CB_FRAG		= 8,	/* the length is the head, the frag data goes at its end */
> +	CB_IFINDEX	= 16,	/* the result is the ifindex of the receiving veth */
> +	CB_DSTMAC	= 32,	/* the frame goes to the MAC of the receiving veth */
> +	CB_NOBLIND	= 64,	/* blinding grows the converted jump past 16 bits */
> +	CB_RAW		= 128,	/* the returns stay, the kept length shows the result */
> +};
> +
> +struct cbpf_run {
> +	int len;
> +	__u32 result;
> +};
> +
> +struct cbpf_case {
> +	const char *descr;
> +	const struct sock_filter *insns;
> +	int cnt;
> +	int (*fill)(struct sock_filter *insns);
> +	const __u8 *data;
> +	int data_len;
> +	const __u8 *frag;
> +	int frag_len;
> +	const struct cbpf_run *runs;
> +	int nr_runs;
> +	unsigned int flags;
> +};
> +
> +#define INSNS(...)							\
> +	(const struct sock_filter[]){ __VA_ARGS__ },			\
> +	.cnt = ARRAY_SIZE(((const struct sock_filter[]){ __VA_ARGS__ }))
> +#define DATA(...)							\
> +	(const __u8[]){ __VA_ARGS__ },					\
> +	.data_len = sizeof((const __u8[]){ __VA_ARGS__ })
> +#define FRAG(...)							\
> +	(const __u8[]){ __VA_ARGS__ },					\
> +	.frag_len = sizeof((const __u8[]){ __VA_ARGS__ })
> +#define RUNS(...)							\
> +	(const struct cbpf_run[]){ __VA_ARGS__ },			\
> +	.nr_runs = ARRAY_SIZE(((const struct cbpf_run[]){ __VA_ARGS__ }))
> +
> +/* The uapi forms are initializers, these are values */
> +#define STMT(code, k)		((struct sock_filter)BPF_STMT(code, k))
> +#define JUMP(code, k, jt, jf)	((struct sock_filter)BPF_JUMP(code, k, jt, jf))
> +
> +/* Jumps with gaps in between, the former __bpf_fill_ja() */
> +static int fill_ja(struct sock_filter *insns, int len, int plen)
> +{
> +	int rlen = (len % plen) - 1, i, j;
> +
> +	for (i = 0; i + plen < len; i += plen)
> +		for (j = 0; j < plen; j++)
> +			insns[i + j] = JUMP(BPF_JMP | BPF_JA, plen - 1 - j, 0, 0);
> +	for (j = 0; j < rlen; j++)
> +		insns[i + j] = JUMP(BPF_JMP | BPF_JA, rlen - 1 - j, 0, 0);
> +	insns[len - 1] = STMT(BPF_RET | BPF_K, 0xababcbac);
> +	return len;
> +}
> +
> +static int fill_ja_short(struct sock_filter *insns)
> +{
> +	return fill_ja(insns, 12, 9);
> +}
> +
> +static int fill_ja_long(struct sock_filter *insns)
> +{
> +	return fill_ja(insns, BPF_MAXINSNS, 68);
> +}
> +
> +static int fill_too_many(struct sock_filter *insns)
> +{
> +	int i;
> +
> +	for (i = 0; i <= BPF_MAXINSNS; i++)
> +		insns[i] = STMT(BPF_RET | BPF_K, 0xfefefefe);
> +	return BPF_MAXINSNS + 1;
> +}
> +
> +/*
> + * A jump over MSH loads, each of which becomes 18 instructions: the
> + * converted offset has to fit 16 bits, which 1820 of them do.
> + */
> +static int fill_jump_over_msh(struct sock_filter *insns, int nr)
> +{
> +	int i;
> +
> +	insns[0] = JUMP(BPF_JMP | BPF_JA, nr, 0, 0);
> +	for (i = 1; i <= nr; i++)
> +		insns[i] = STMT(BPF_LDX | BPF_B | BPF_MSH, 0);
> +	insns[nr + 1] = STMT(BPF_RET | BPF_K, 0xabababab);
> +	return nr + 2;
> +}
> +
> +static int fill_jump_over_all_msh(struct sock_filter *insns)
> +{
> +	return fill_jump_over_msh(insns, BPF_MAXINSNS - 2);
> +}
> +
> +static int fill_jump_over_1821_msh(struct sock_filter *insns)
> +{
> +	return fill_jump_over_msh(insns, 1821);
> +}
> +
> +static int fill_jump_over_1820_msh(struct sock_filter *insns)
> +{
> +	return fill_jump_over_msh(insns, 1820);
> +}
> +
> +static int fill_empty(struct sock_filter *insns)
> +{
> +	return 0;
> +}
> +
> +/* A conditional jump past 32K converted instructions */
> +static int fill_late_jump(struct sock_filter *insns)
> +{
> +	int i;
> +
> +	for (i = 0; i < 2000; i++)
> +		insns[i] = STMT(BPF_LDX | BPF_B | BPF_MSH, 0);
> +	insns[2000] = STMT(BPF_LD | BPF_IMM, 1);
> +	insns[2001] = JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1);
> +	insns[2002] = STMT(BPF_RET | BPF_K, 1);
> +	insns[2003] = STMT(BPF_RET | BPF_K, 2);
> +	return 2004;
> +}
> +
> +/* A conditional jump over 254 loads, which expand to 12 instructions each */
> +static int fill_jump_over_ld_abs(struct sock_filter *insns, bool taken)
> +{
> +	int i;
> +
> +	insns[0] = STMT(BPF_LD | BPF_B | BPF_ABS, 0);
> +	insns[1] = JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, taken ? 255 : 0, taken ? 0 : 255);
> +	for (i = 2; i < 256; i++)
> +		insns[i] = STMT(BPF_LD | BPF_B | BPF_ABS, 0);
> +	insns[256] = STMT(BPF_RET | BPF_K, 2);
> +	insns[257] = STMT(BPF_RET | BPF_K, 3);
> +	return 258;
> +}
> +
> +static int fill_jt_over_ld_abs(struct sock_filter *insns)
> +{
> +	return fill_jump_over_ld_abs(insns, true);
> +}
> +
> +static int fill_jf_over_ld_abs(struct sock_filter *insns)
> +{
> +	return fill_jump_over_ld_abs(insns, false);
> +}
> +
> +/* The result is small enough for the kept length to show it as is */
> +static int fill_all_msh(struct sock_filter *insns)
> +{
> +	int len = BPF_MAXINSNS, i;
> +
> +	for (i = 0; i < len - 3; i++)
> +		insns[i] = STMT(BPF_LDX | BPF_B | BPF_MSH, 0);
> +	insns[len - 3] = STMT(BPF_LD | BPF_IMM, 0x2a);
> +	insns[len - 2] = STMT(BPF_ALU | BPF_XOR | BPF_X, 0);
> +	insns[len - 1] = STMT(BPF_RET | BPF_A, 0);
> +	return len;
> +}
> +
> +static int fill_ld_abs_cpu(struct sock_filter *insns)
> +{
> +	int len = BPF_MAXINSNS, i;
> +
> +	for (i = 0; i < len - 1; i += 2) {
> +		insns[i] = STMT(BPF_LD | BPF_B | BPF_ABS, 0);
> +		insns[i + 1] = STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_CPU);
> +	}
> +	insns[len - 1] = STMT(BPF_RET | BPF_K, 0xbee);
> +	return len;
> +}
> +
> +static const struct cbpf_case cases[] = {
> +	{
> +		.descr = "TAX",
> +		.insns = INSNS(
> +			BPF_STMT(BPF_LD | BPF_IMM, 1),
> +			BPF_STMT(BPF_MISC | BPF_TAX, 0),
> +			BPF_STMT(BPF_LD | BPF_IMM, 2),
> +			BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
> +			BPF_STMT(BPF_ALU | BPF_NEG, 0), /* A == -3 */
> +			BPF_STMT(BPF_MISC | BPF_TAX, 0),
> +			BPF_STMT(BPF_LD | BPF_LEN, 0),
> +			BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
> +			BPF_STMT(BPF_MISC | BPF_TAX, 0), /* X == len - 3 */
> +			BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
> +			BPF_STMT(BPF_RET | BPF_A, 0)
> +		),
> +		.data = DATA(10, 20, 30, 40, 50),
> +		.runs = RUNS({ 2, 10 }, { 3, 20 }, { 4, 30 }),
> +	},
> +	{
> +		.descr = "TXA",
> +		.insns = INSNS(
> +			BPF_STMT(BPF_LDX | BPF_LEN, 0),
> +			BPF_STMT(BPF_MISC | BPF_TXA, 0),
> +			BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
> +			BPF_STMT(BPF_RET | BPF_A, 0) /* A == len * 2 */
> +		),
> +		.data = DATA(10, 20, 30, 40, 50),
> +		.runs = RUNS({ 1, 2 }, { 3, 6 }, { 4, 8 }),
> +	},

I don't like it. It just copies old test_lib.ko into test_progs.

I'd prefer to keep cbpf untested. It's not like anyone is working on it.

pw-bot: cr

  parent reply	other threads:[~2026-10-15 19:08 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15 17:19 [PATCH bpf-next 1/2] selftests/bpf: Follow-up coverage to JIT instruction tests Daniel Borkmann
2026-10-15 17:19 ` [PATCH bpf-next 2/2] selftests/bpf: Add JIT cBPF-related " Daniel Borkmann
2026-10-15 17:31   ` sashiko-bot
2026-10-15 18:08   ` bot+bpf-ci
2026-10-15 19:08   ` Alexei Starovoitov [this message]
2026-10-15 19:11     ` Daniel Borkmann
2026-10-15 18:08 ` [PATCH bpf-next 1/2] selftests/bpf: Follow-up coverage to JIT " bot+bpf-ci

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=DM5NCJVIY5GR.3KH59CI540I82@gmail.com \
    --to=alexei.starovoitov@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=memxor@gmail.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