BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions
@ 2026-08-30  7:31 Siddharth Chintamaneni
  2026-08-30  7:31 ` [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test Siddharth Chintamaneni
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Siddharth Chintamaneni @ 2026-08-30  7:31 UTC (permalink / raw)
  To: bpf
  Cc: Siddharth Chintamaneni, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
	Anton Protopopov, Puranjay Mohan, rachelmenge, hargar, apais

check_subprogs() treats gotox as a direct jump and validates its reserved
zero offset. When gotox is the final instruction, this produces a
synthetic successor one instruction past the end of the subprogram and
rejects an otherwise valid program.

Skip direct-offset validation for gotox and accept it as a
non-fallthrough terminal instruction. Its actual targets remain validated
from the instruction-array jump table during CFG construction.

Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
---
 kernel/bpf/verifier.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e036ae20bf6b..44195ec1445d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3087,6 +3087,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			subprog[cur_subprog].exit_idx = i;
 			goto next;
 		}
+		if (insn_is_gotox(&insn[i]))
+			goto next;
 		off = i + bpf_jmp_offset(&insn[i]) + 1;
 		if (off < subprog_start || off >= subprog_end) {
 			verbose(env, "jump out of range from insn %d to %d\n", i, off);
@@ -3106,7 +3108,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
 			 */
 			if (code != (BPF_JMP | BPF_EXIT) &&
 			    code != (BPF_JMP32 | BPF_JA) &&
-			    code != (BPF_JMP | BPF_JA)) {
+			    code != (BPF_JMP | BPF_JA) &&
+			    !insn_is_gotox(&insn[i])) {
 				verbose(env, "last insn is not an exit or jmp\n");
 				bpf_diag_program_structure(
 					env, i, "subprogram can fall through",

base-commit: cd35e1b10182c42b4ae31ee49119463b17d8ba7f
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test
  2026-08-30  7:31 [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions Siddharth Chintamaneni
@ 2026-08-30  7:31 ` Siddharth Chintamaneni
  2026-08-31 12:34   ` Anton Protopopov
  2026-08-30  7:45 ` [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions sashiko-bot
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Siddharth Chintamaneni @ 2026-08-30  7:31 UTC (permalink / raw)
  To: bpf
  Cc: Siddharth Chintamaneni, Alexei Starovoitov, Daniel Borkmann,
	John Fastabend, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
	Yonghong Song, Jiri Olsa, Emil Tsalapatis, Ihor Solodrai,
	Anton Protopopov, Puranjay Mohan, rachelmenge, hargar, apais

Add coverage for the instruction layout emitted by LLVM for a computed
goto. The jump-table targets precede the dispatch block and gotox is the
final instruction in the subprogram.

An equivalent unoptimized C source is:

    int bpf_prog_trigger_syscall_prog(void *ctx)
    {
            __label__ l1, l2;
            void *tgt;
            int ret = 0;

            if (ctx)
                    tgt = &&l1;
            else
                    tgt = &&l2;
            goto *tgt;
    l1:
            ret += 1;
    l2:
            ret += 2;
            return 0;
    }

Tested:

    #646/5 verifier_gotox/jump_table_compiler_layout:OK
    #646 verifier_gotox:OK
    Summary: 1/28 PASSED, 0 SKIPPED, 0/0 FAILED

Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
---
 .../selftests/bpf/progs/verifier_gotox.c      | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/tools/testing/selftests/bpf/progs/verifier_gotox.c b/tools/testing/selftests/bpf/progs/verifier_gotox.c
index 5b18c9a27717..0ea445287175 100644
--- a/tools/testing/selftests/bpf/progs/verifier_gotox.c
+++ b/tools/testing/selftests/bpf/progs/verifier_gotox.c
@@ -47,6 +47,61 @@ DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_src_reg,      BPF_REG_1, 0, 0, __fa
 DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_off, BPF_REG_0, 1, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
 DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_imm, BPF_REG_0, 0, 1, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
 
+SEC("socket")
+__success __retval(0)
+__naked void jump_table_compiler_layout(void)
+{
+	asm volatile ("						        \
+	.pushsection .jumptables,\"\",@progbits;			\
+jt_l1_%=:								\
+	.quad l1_%= - socket;						\
+	.size jt_l1_%=, 8;						\
+	.global jt_l1_%=;						\
+jt_l2_%=:								\
+	.quad l2_%= - socket;						\
+	.size jt_l2_%=, 8;						\
+	.global jt_l2_%=;						\
+	.popsection;							\
+									\
+	*(u64 *)(r10 - 8) = r1;						\
+	*(u32 *)(r10 - 20) = 0;						\
+	r1 = *(u64 *)(r10 - 8);						\
+	if r1 == 0 goto select_l2_%=;					\
+	goto select_l1_%=;						\
+select_l1_%=:								\
+	r1 = jt_l1_%= ll;						\
+	r1 = *(u64 *)(r1 + 0);						\
+	*(u64 *)(r10 - 16) = r1;					\
+	goto selected_%=;						\
+select_l2_%=:								\
+	r1 = jt_l2_%= ll;						\
+	r1 = *(u64 *)(r1 + 0);						\
+	*(u64 *)(r10 - 16) = r1;					\
+	goto selected_%=;						\
+selected_%=:								\
+	r1 = *(u64 *)(r10 - 16);					\
+	*(u64 *)(r10 - 32) = r1;					\
+	goto dispatch_%=;						\
+l1_%=:									\
+	w1 = *(u32 *)(r10 - 20);					\
+	w1 += 1;							\
+	*(u32 *)(r10 - 20) = w1;					\
+	goto l2_%=;							\
+l2_%=:									\
+	w1 = *(u32 *)(r10 - 20);					\
+	w1 += 2;							\
+	*(u32 *)(r10 - 20) = w1;					\
+	w0 = 0;								\
+	exit;								\
+dispatch_%=:								\
+	r1 = *(u64 *)(r10 - 32);					\
+	.8byte %[gotox_r1];						\
+"	:
+	: __imm_insn(gotox_r1, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X,
+					    BPF_REG_1, 0, 0, 0))
+	: __clobber_all);
+}
+
 /*
  * Gotox is forbidden when there is no jump table loaded
  * which points to the sub-function where the gotox is used
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions
  2026-08-30  7:31 [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions Siddharth Chintamaneni
  2026-08-30  7:31 ` [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test Siddharth Chintamaneni
@ 2026-08-30  7:45 ` sashiko-bot
  2026-08-30  8:34 ` bot+bpf-ci
  2026-08-31 12:21 ` Anton Protopopov
  3 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-08-30  7:45 UTC (permalink / raw)
  To: Siddharth Chintamaneni; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] bpf_throw kfunc is incorrectly rejected as the last instruction of a subprogram.
--

commit 2856cb56c044a060705479701aa63316b773e7de
Author: Siddharth Chintamaneni <sidchintamaneni@gmail.com>

bpf: allow terminal gotox instructions

This commit skips direct-offset validation for gotox instructions in
check_subprogs() and accepts them as non-fallthrough terminal instructions.
The actual targets are validated during CFG construction instead.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index e036ae20bf6b9..44195ec1445d9 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -3106,7 +3108,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
>  			 */
>  			if (code != (BPF_JMP | BPF_EXIT) &&
>  			    code != (BPF_JMP32 | BPF_JA) &&
> -			    code != (BPF_JMP | BPF_JA)) {
> +			    code != (BPF_JMP | BPF_JA) &&
> +			    !insn_is_gotox(&insn[i])) {

[Severity: High]
This isn't a bug introduced by this patch, but does check_subprogs()
incorrectly reject bpf_throw calls when they are used as terminal instructions?

The comment just above this block explicitly states that the last instruction
of the subprogram can be a bpf_throw call.

However, the if condition only allows exits, unconditional jumps, and
gotox instructions.

Because there is no check allowing bpf_throw (like checking if the call is a
throw kfunc), will a valid BPF program ending with a bpf_throw instruction be
incorrectly rejected during load when it hits this validation?

>  				verbose(env, "last insn is not an exit or jmp\n");
>  				bpf_diag_program_structure(
>  					env, i, "subprogram can fall through",

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260830073125.360934-1-sidchintamaneni@gmail.com?part=1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions
  2026-08-30  7:31 [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions Siddharth Chintamaneni
  2026-08-30  7:31 ` [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test Siddharth Chintamaneni
  2026-08-30  7:45 ` [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions sashiko-bot
@ 2026-08-30  8:34 ` bot+bpf-ci
  2026-08-31 12:21 ` Anton Protopopov
  3 siblings, 0 replies; 6+ messages in thread
From: bot+bpf-ci @ 2026-08-30  8:34 UTC (permalink / raw)
  To: sidchintamaneni, bpf
  Cc: sidchintamaneni, ast, daniel, john.fastabend, andrii, eddyz87,
	memxor, martin.lau, song, yonghong.song, jolsa, emil,
	ihor.solodrai, a.s.protopopov, puranjay, rachelmenge, hargar,
	apais, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song,
	mason, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 1272 bytes --]

> bpf: allow terminal gotox instructions
>
> check_subprogs() treats gotox as a direct jump and validates its reserved
> zero offset. When gotox is the final instruction, this produces a
> synthetic successor one instruction past the end of the subprogram and
> rejects an otherwise valid program.
>
> Skip direct-offset validation for gotox and accept it as a
> non-fallthrough terminal instruction. Its actual targets remain validated
> from the instruction-array jump table during CFG construction.
>
> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>

This looks like a bug fix for gotox instruction handling. The bug was
introduced when gotox instructions were added but check_subprogs() was not
updated to handle them. When gotox is the final instruction of a
subprogram, check_subprogs() incorrectly validates its reserved zero offset
as a direct jump, producing a synthetic successor past the end of the
subprogram.

Should this include:

  Fixes: 493d9e0d6083 ("bpf, x86: add support for indirect jumps")


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33300101433

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions
  2026-08-30  7:31 [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions Siddharth Chintamaneni
                   ` (2 preceding siblings ...)
  2026-08-30  8:34 ` bot+bpf-ci
@ 2026-08-31 12:21 ` Anton Protopopov
  3 siblings, 0 replies; 6+ messages in thread
From: Anton Protopopov @ 2026-08-31 12:21 UTC (permalink / raw)
  To: Siddharth Chintamaneni
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Puranjay Mohan, rachelmenge,
	hargar, apais

On 26/08/30 07:31AM, Siddharth Chintamaneni wrote:
> check_subprogs() treats gotox as a direct jump and validates its reserved
> zero offset. When gotox is the final instruction, this produces a
> synthetic successor one instruction past the end of the subprogram and
> rejects an otherwise valid program.
> 
> Skip direct-offset validation for gotox and accept it as a
> non-fallthrough terminal instruction. Its actual targets remain validated
> from the instruction-array jump table during CFG construction.

Thanks, the fix looks correct.

I have comments to the selftest, so expecting v2. When you resend it,
please capitalize "allow" in this commit's title.

Reviewed-by: Anton Protopopov <a.s.protopopov@gmail.com>

> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
> ---
>  kernel/bpf/verifier.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index e036ae20bf6b..44195ec1445d 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -3087,6 +3087,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
>  			subprog[cur_subprog].exit_idx = i;
>  			goto next;
>  		}
> +		if (insn_is_gotox(&insn[i]))
> +			goto next;
>  		off = i + bpf_jmp_offset(&insn[i]) + 1;
>  		if (off < subprog_start || off >= subprog_end) {
>  			verbose(env, "jump out of range from insn %d to %d\n", i, off);
> @@ -3106,7 +3108,8 @@ static int check_subprogs(struct bpf_verifier_env *env)
>  			 */
>  			if (code != (BPF_JMP | BPF_EXIT) &&
>  			    code != (BPF_JMP32 | BPF_JA) &&
> -			    code != (BPF_JMP | BPF_JA)) {
> +			    code != (BPF_JMP | BPF_JA) &&
> +			    !insn_is_gotox(&insn[i])) {
>  				verbose(env, "last insn is not an exit or jmp\n");
>  				bpf_diag_program_structure(
>  					env, i, "subprogram can fall through",
> 
> base-commit: cd35e1b10182c42b4ae31ee49119463b17d8ba7f
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test
  2026-08-30  7:31 ` [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test Siddharth Chintamaneni
@ 2026-08-31 12:34   ` Anton Protopopov
  0 siblings, 0 replies; 6+ messages in thread
From: Anton Protopopov @ 2026-08-31 12:34 UTC (permalink / raw)
  To: Siddharth Chintamaneni
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Puranjay Mohan, rachelmenge,
	hargar, apais

On 26/08/30 07:31AM, Siddharth Chintamaneni wrote:
> Add coverage for the instruction layout emitted by LLVM for a computed
> goto. The jump-table targets precede the dispatch block and gotox is the
> final instruction in the subprogram.

I would shorten this, and remove mention of compiler altogether (this
is not a compiler bug after all). Just explain that we add a test
where gotox is the terminal instruction of a program.

> An equivalent unoptimized C source is:
> 
>     int bpf_prog_trigger_syscall_prog(void *ctx)
>     {
>             __label__ l1, l2;
>             void *tgt;
>             int ret = 0;
> 
>             if (ctx)
>                     tgt = &&l1;
>             else
>                     tgt = &&l2;
>             goto *tgt;
>     l1:
>             ret += 1;
>     l2:
>             ret += 2;
>             return 0;
>     }

This is absolutely not obvious why this must necessarily 
translate to the test you're added. Also, again, this tests
the verification, not code generation. Just omit this C chunk.

> 
> Tested:
> 
>     #646/5 verifier_gotox/jump_table_compiler_layout:OK
>     #646 verifier_gotox:OK
>     Summary: 1/28 PASSED, 0 SKIPPED, 0/0 FAILED

This output is not adding any information, please omit it.

> 
> Signed-off-by: Siddharth Chintamaneni <sidchintamaneni@gmail.com>
> ---
>  .../selftests/bpf/progs/verifier_gotox.c      | 55 +++++++++++++++++++
>  1 file changed, 55 insertions(+)
> 
> diff --git a/tools/testing/selftests/bpf/progs/verifier_gotox.c b/tools/testing/selftests/bpf/progs/verifier_gotox.c
> index 5b18c9a27717..0ea445287175 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_gotox.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_gotox.c
> @@ -47,6 +47,61 @@ DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_src_reg,      BPF_REG_1, 0, 0, __fa
>  DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_off, BPF_REG_0, 1, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
>  DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_imm, BPF_REG_0, 0, 1, __failure __msg("BPF_JA|BPF_X uses reserved fields"))
>  
> +SEC("socket")
> +__success __retval(0)
> +__naked void jump_table_compiler_layout(void)
> +{
> +	asm volatile ("						        \
> +	.pushsection .jumptables,\"\",@progbits;			\
> +jt_l1_%=:								\
> +	.quad l1_%= - socket;						\
> +	.size jt_l1_%=, 8;						\
> +	.global jt_l1_%=;						\
> +jt_l2_%=:								\
> +	.quad l2_%= - socket;						\
> +	.size jt_l2_%=, 8;						\
> +	.global jt_l2_%=;						\
> +	.popsection;							\
> +									\
> +	*(u64 *)(r10 - 8) = r1;						\
> +	*(u32 *)(r10 - 20) = 0;						\
> +	r1 = *(u64 *)(r10 - 8);						\
> +	if r1 == 0 goto select_l2_%=;					\
> +	goto select_l1_%=;						\
> +select_l1_%=:								\
> +	r1 = jt_l1_%= ll;						\
> +	r1 = *(u64 *)(r1 + 0);						\
> +	*(u64 *)(r10 - 16) = r1;					\
> +	goto selected_%=;						\
> +select_l2_%=:								\
> +	r1 = jt_l2_%= ll;						\
> +	r1 = *(u64 *)(r1 + 0);						\
> +	*(u64 *)(r10 - 16) = r1;					\
> +	goto selected_%=;						\
> +selected_%=:								\
> +	r1 = *(u64 *)(r10 - 16);					\
> +	*(u64 *)(r10 - 32) = r1;					\
> +	goto dispatch_%=;						\
> +l1_%=:									\
> +	w1 = *(u32 *)(r10 - 20);					\
> +	w1 += 1;							\
> +	*(u32 *)(r10 - 20) = w1;					\
> +	goto l2_%=;							\
> +l2_%=:									\
> +	w1 = *(u32 *)(r10 - 20);					\
> +	w1 += 2;							\
> +	*(u32 *)(r10 - 20) = w1;					\
> +	w0 = 0;								\
> +	exit;								\
> +dispatch_%=:								\
> +	r1 = *(u64 *)(r10 - 32);					\
> +	.8byte %[gotox_r1];						\
> +"	:
> +	: __imm_insn(gotox_r1, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X,
> +					    BPF_REG_1, 0, 0, 0))
> +	: __clobber_all);
> +}

This assembly can be simplified, no reason to pass something
generated from C. Just create a JT with one target, and jump
to it from the end. Something around

      0: rX = &jump_table  /* jump_table[1] = { &&ret, } */
      1: rX = *rX
      2: goto end
    ret:
      3: return 0
    end:
      4: gotox *rX

Also, if you want a full selftest coverage, another selftest should
check the same thing for a subprogram, not the main prog.

> +
>  /*
>   * Gotox is forbidden when there is no jump table loaded
>   * which points to the sub-function where the gotox is used
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-08-31 12:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-30  7:31 [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions Siddharth Chintamaneni
2026-08-30  7:31 ` [PATCH bpf-next v1 2/2] selftests/bpf: Add compiler-layout terminal gotox test Siddharth Chintamaneni
2026-08-31 12:34   ` Anton Protopopov
2026-08-30  7:45 ` [PATCH bpf-next v1 1/2] bpf: allow terminal gotox instructions sashiko-bot
2026-08-30  8:34 ` bot+bpf-ci
2026-08-31 12:21 ` Anton Protopopov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox