Skip to content

Commit 3f3c2f0

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-allow-may_goto-0-instruction'
Yonghong Song says: ==================== Emil Tsalapatis from Meta reported such a case where 'may_goto 0' insn is generated by clang-19 compiler and this caused verification failure since 'may_goto 0' is rejected by verifier. In fact, 'may_goto 0' insn is actually a no-op and it won't hurt verification. The only side effect is that the verifier will convert the insn to a sequence of codes like /* r10 - 8 stores the implicit loop count */ r11 = *(u64 *)(r10 -8) if r11 == 0x0 goto pc+2 r11 -= 1 *(u64 *)(r10 -8) = r11 With this patch set 'may_goto 0' insns are allowed in verification which also removes those insns. Changelogs: v1 -> v2: - Instead of a separate function, removing 'may_goto 0' in existing func opt_remove_nops(). ==================== Link: https://patch.msgid.link/20250118192019.2123689-1-yonghong.song@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents d10cafc + 14a627f commit 3f3c2f0

File tree

4 files changed

+139
-6
lines changed

4 files changed

+139
-6
lines changed

kernel/bpf/verifier.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15972,9 +15972,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1597215972

1597315973
if (insn->code != (BPF_JMP | BPF_JCOND) ||
1597415974
insn->src_reg != BPF_MAY_GOTO ||
15975-
insn->dst_reg || insn->imm || insn->off == 0) {
15976-
verbose(env, "invalid may_goto off %d imm %d\n",
15977-
insn->off, insn->imm);
15975+
insn->dst_reg || insn->imm) {
15976+
verbose(env, "invalid may_goto imm %d\n", insn->imm);
1597815977
return -EINVAL;
1597915978
}
1598015979
prev_st = find_prev_entry(env, cur_st->parent, idx);
@@ -20185,23 +20184,28 @@ static int opt_remove_dead_code(struct bpf_verifier_env *env)
2018520184
}
2018620185

2018720186
static const struct bpf_insn NOP = BPF_JMP_IMM(BPF_JA, 0, 0, 0);
20187+
static const struct bpf_insn MAY_GOTO_0 = BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0, 0);
2018820188

2018920189
static int opt_remove_nops(struct bpf_verifier_env *env)
2019020190
{
20191-
const struct bpf_insn ja = NOP;
2019220191
struct bpf_insn *insn = env->prog->insnsi;
2019320192
int insn_cnt = env->prog->len;
20193+
bool is_may_goto_0, is_ja;
2019420194
int i, err;
2019520195

2019620196
for (i = 0; i < insn_cnt; i++) {
20197-
if (memcmp(&insn[i], &ja, sizeof(ja)))
20197+
is_may_goto_0 = !memcmp(&insn[i], &MAY_GOTO_0, sizeof(MAY_GOTO_0));
20198+
is_ja = !memcmp(&insn[i], &NOP, sizeof(NOP));
20199+
20200+
if (!is_may_goto_0 && !is_ja)
2019820201
continue;
2019920202

2020020203
err = verifier_remove_insns(env, i, 1);
2020120204
if (err)
2020220205
return err;
2020320206
insn_cnt--;
20204-
i--;
20207+
/* Go back one insn to catch may_goto +1; may_goto +0 sequence */
20208+
i -= (is_may_goto_0 && i > 0) ? 2 : 1;
2020520209
}
2020620210

2020720211
return 0;

tools/testing/selftests/bpf/prog_tests/verifier.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
#include "verifier_map_ptr_mixing.skel.h"
5353
#include "verifier_map_ret_val.skel.h"
5454
#include "verifier_masking.skel.h"
55+
#include "verifier_may_goto_1.skel.h"
56+
#include "verifier_may_goto_2.skel.h"
5557
#include "verifier_meta_access.skel.h"
5658
#include "verifier_movsx.skel.h"
5759
#include "verifier_mtu.skel.h"
@@ -182,6 +184,8 @@ void test_verifier_map_ptr(void) { RUN(verifier_map_ptr); }
182184
void test_verifier_map_ptr_mixing(void) { RUN(verifier_map_ptr_mixing); }
183185
void test_verifier_map_ret_val(void) { RUN(verifier_map_ret_val); }
184186
void test_verifier_masking(void) { RUN(verifier_masking); }
187+
void test_verifier_may_goto_1(void) { RUN(verifier_may_goto_1); }
188+
void test_verifier_may_goto_2(void) { RUN(verifier_may_goto_2); }
185189
void test_verifier_meta_access(void) { RUN(verifier_meta_access); }
186190
void test_verifier_movsx(void) { RUN(verifier_movsx); }
187191
void test_verifier_netfilter_ctx(void) { RUN(verifier_netfilter_ctx); }
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
4+
#include <linux/bpf.h>
5+
#include <bpf/bpf_helpers.h>
6+
#include "../../../include/linux/filter.h"
7+
#include "bpf_misc.h"
8+
9+
SEC("raw_tp")
10+
__description("may_goto 0")
11+
__arch_x86_64
12+
__xlated("0: r0 = 1")
13+
__xlated("1: exit")
14+
__success
15+
__naked void may_goto_simple(void)
16+
{
17+
asm volatile (
18+
".8byte %[may_goto];"
19+
"r0 = 1;"
20+
".8byte %[may_goto];"
21+
"exit;"
22+
:
23+
: __imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
24+
: __clobber_all);
25+
}
26+
27+
SEC("raw_tp")
28+
__description("batch 2 of may_goto 0")
29+
__arch_x86_64
30+
__xlated("0: r0 = 1")
31+
__xlated("1: exit")
32+
__success
33+
__naked void may_goto_batch_0(void)
34+
{
35+
asm volatile (
36+
".8byte %[may_goto1];"
37+
".8byte %[may_goto1];"
38+
"r0 = 1;"
39+
".8byte %[may_goto1];"
40+
".8byte %[may_goto1];"
41+
"exit;"
42+
:
43+
: __imm_insn(may_goto1, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
44+
: __clobber_all);
45+
}
46+
47+
SEC("raw_tp")
48+
__description("may_goto batch with offsets 2/1/0")
49+
__arch_x86_64
50+
__xlated("0: r0 = 1")
51+
__xlated("1: exit")
52+
__success
53+
__naked void may_goto_batch_1(void)
54+
{
55+
asm volatile (
56+
".8byte %[may_goto1];"
57+
".8byte %[may_goto2];"
58+
".8byte %[may_goto3];"
59+
"r0 = 1;"
60+
".8byte %[may_goto1];"
61+
".8byte %[may_goto2];"
62+
".8byte %[may_goto3];"
63+
"exit;"
64+
:
65+
: __imm_insn(may_goto1, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 2 /* offset */, 0)),
66+
__imm_insn(may_goto2, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 1 /* offset */, 0)),
67+
__imm_insn(may_goto3, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
68+
: __clobber_all);
69+
}
70+
71+
SEC("raw_tp")
72+
__description("may_goto batch with offsets 2/0")
73+
__arch_x86_64
74+
__xlated("0: *(u64 *)(r10 -8) = 8388608")
75+
__xlated("1: r11 = *(u64 *)(r10 -8)")
76+
__xlated("2: if r11 == 0x0 goto pc+3")
77+
__xlated("3: r11 -= 1")
78+
__xlated("4: *(u64 *)(r10 -8) = r11")
79+
__xlated("5: r0 = 1")
80+
__xlated("6: r0 = 2")
81+
__xlated("7: exit")
82+
__success
83+
__naked void may_goto_batch_2(void)
84+
{
85+
asm volatile (
86+
".8byte %[may_goto1];"
87+
".8byte %[may_goto3];"
88+
"r0 = 1;"
89+
"r0 = 2;"
90+
"exit;"
91+
:
92+
: __imm_insn(may_goto1, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 2 /* offset */, 0)),
93+
__imm_insn(may_goto3, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
94+
: __clobber_all);
95+
}
96+
97+
char _license[] SEC("license") = "GPL";
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
4+
#include "bpf_misc.h"
5+
#include "bpf_experimental.h"
6+
7+
int gvar;
8+
9+
SEC("raw_tp")
10+
__description("C code with may_goto 0")
11+
__success
12+
int may_goto_c_code(void)
13+
{
14+
int i, tmp[3];
15+
16+
for (i = 0; i < 3 && can_loop; i++)
17+
tmp[i] = 0;
18+
19+
for (i = 0; i < 3 && can_loop; i++)
20+
tmp[i] = gvar - i;
21+
22+
for (i = 0; i < 3 && can_loop; i++)
23+
gvar += tmp[i];
24+
25+
return 0;
26+
}
27+
28+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)