Skip to content

Commit

Permalink
Merge branch 'bpf-allow-may_goto-0-instruction'
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alexei Starovoitov committed Jan 20, 2025
2 parents d10cafc + 14a627f commit 3f3c2f0
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 6 deletions.
16 changes: 10 additions & 6 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -15972,9 +15972,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,

if (insn->code != (BPF_JMP | BPF_JCOND) ||
insn->src_reg != BPF_MAY_GOTO ||
insn->dst_reg || insn->imm || insn->off == 0) {
verbose(env, "invalid may_goto off %d imm %d\n",
insn->off, insn->imm);
insn->dst_reg || insn->imm) {
verbose(env, "invalid may_goto imm %d\n", insn->imm);
return -EINVAL;
}
prev_st = find_prev_entry(env, cur_st->parent, idx);
Expand Down Expand Up @@ -20185,23 +20184,28 @@ static int opt_remove_dead_code(struct bpf_verifier_env *env)
}

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

static int opt_remove_nops(struct bpf_verifier_env *env)
{
const struct bpf_insn ja = NOP;
struct bpf_insn *insn = env->prog->insnsi;
int insn_cnt = env->prog->len;
bool is_may_goto_0, is_ja;
int i, err;

for (i = 0; i < insn_cnt; i++) {
if (memcmp(&insn[i], &ja, sizeof(ja)))
is_may_goto_0 = !memcmp(&insn[i], &MAY_GOTO_0, sizeof(MAY_GOTO_0));
is_ja = !memcmp(&insn[i], &NOP, sizeof(NOP));

if (!is_may_goto_0 && !is_ja)
continue;

err = verifier_remove_insns(env, i, 1);
if (err)
return err;
insn_cnt--;
i--;
/* Go back one insn to catch may_goto +1; may_goto +0 sequence */
i -= (is_may_goto_0 && i > 0) ? 2 : 1;
}

return 0;
Expand Down
4 changes: 4 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
#include "verifier_map_ptr_mixing.skel.h"
#include "verifier_map_ret_val.skel.h"
#include "verifier_masking.skel.h"
#include "verifier_may_goto_1.skel.h"
#include "verifier_may_goto_2.skel.h"
#include "verifier_meta_access.skel.h"
#include "verifier_movsx.skel.h"
#include "verifier_mtu.skel.h"
Expand Down Expand Up @@ -182,6 +184,8 @@ void test_verifier_map_ptr(void) { RUN(verifier_map_ptr); }
void test_verifier_map_ptr_mixing(void) { RUN(verifier_map_ptr_mixing); }
void test_verifier_map_ret_val(void) { RUN(verifier_map_ret_val); }
void test_verifier_masking(void) { RUN(verifier_masking); }
void test_verifier_may_goto_1(void) { RUN(verifier_may_goto_1); }
void test_verifier_may_goto_2(void) { RUN(verifier_may_goto_2); }
void test_verifier_meta_access(void) { RUN(verifier_meta_access); }
void test_verifier_movsx(void) { RUN(verifier_movsx); }
void test_verifier_netfilter_ctx(void) { RUN(verifier_netfilter_ctx); }
Expand Down
97 changes: 97 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_may_goto_1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "../../../include/linux/filter.h"
#include "bpf_misc.h"

SEC("raw_tp")
__description("may_goto 0")
__arch_x86_64
__xlated("0: r0 = 1")
__xlated("1: exit")
__success
__naked void may_goto_simple(void)
{
asm volatile (
".8byte %[may_goto];"
"r0 = 1;"
".8byte %[may_goto];"
"exit;"
:
: __imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
: __clobber_all);
}

SEC("raw_tp")
__description("batch 2 of may_goto 0")
__arch_x86_64
__xlated("0: r0 = 1")
__xlated("1: exit")
__success
__naked void may_goto_batch_0(void)
{
asm volatile (
".8byte %[may_goto1];"
".8byte %[may_goto1];"
"r0 = 1;"
".8byte %[may_goto1];"
".8byte %[may_goto1];"
"exit;"
:
: __imm_insn(may_goto1, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
: __clobber_all);
}

SEC("raw_tp")
__description("may_goto batch with offsets 2/1/0")
__arch_x86_64
__xlated("0: r0 = 1")
__xlated("1: exit")
__success
__naked void may_goto_batch_1(void)
{
asm volatile (
".8byte %[may_goto1];"
".8byte %[may_goto2];"
".8byte %[may_goto3];"
"r0 = 1;"
".8byte %[may_goto1];"
".8byte %[may_goto2];"
".8byte %[may_goto3];"
"exit;"
:
: __imm_insn(may_goto1, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 2 /* offset */, 0)),
__imm_insn(may_goto2, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 1 /* offset */, 0)),
__imm_insn(may_goto3, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
: __clobber_all);
}

SEC("raw_tp")
__description("may_goto batch with offsets 2/0")
__arch_x86_64
__xlated("0: *(u64 *)(r10 -8) = 8388608")
__xlated("1: r11 = *(u64 *)(r10 -8)")
__xlated("2: if r11 == 0x0 goto pc+3")
__xlated("3: r11 -= 1")
__xlated("4: *(u64 *)(r10 -8) = r11")
__xlated("5: r0 = 1")
__xlated("6: r0 = 2")
__xlated("7: exit")
__success
__naked void may_goto_batch_2(void)
{
asm volatile (
".8byte %[may_goto1];"
".8byte %[may_goto3];"
"r0 = 1;"
"r0 = 2;"
"exit;"
:
: __imm_insn(may_goto1, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 2 /* offset */, 0)),
__imm_insn(may_goto3, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0 /* offset */, 0))
: __clobber_all);
}

char _license[] SEC("license") = "GPL";
28 changes: 28 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_may_goto_2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#include "bpf_misc.h"
#include "bpf_experimental.h"

int gvar;

SEC("raw_tp")
__description("C code with may_goto 0")
__success
int may_goto_c_code(void)
{
int i, tmp[3];

for (i = 0; i < 3 && can_loop; i++)
tmp[i] = 0;

for (i = 0; i < 3 && can_loop; i++)
tmp[i] = gvar - i;

for (i = 0; i < 3 && can_loop; i++)
gvar += tmp[i];

return 0;
}

char _license[] SEC("license") = "GPL";

0 comments on commit 3f3c2f0

Please sign in to comment.