Skip to content

Commit 3feb263

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: handle ldimm64 properly in check_cfg()
ldimm64 instructions are 16-byte long, and so have to be handled appropriately in check_cfg(), just like the rest of BPF verifier does. This has implications in three places: - when determining next instruction for non-jump instructions; - when determining next instruction for callback address ldimm64 instructions (in visit_func_call_insn()); - when checking for unreachable instructions, where second half of ldimm64 is expected to be unreachable; We take this also as an opportunity to report jump into the middle of ldimm64. And adjust few test_verifier tests accordingly. Acked-by: Eduard Zingerman <eddyz87@gmail.com> Reported-by: Hao Sun <sunhao.th@gmail.com> Fixes: 475fb78 ("bpf: verifier (add branch/goto checks)") Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20231110002638.4168352-2-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent fe69a1b commit 3feb263

3 files changed

Lines changed: 30 additions & 13 deletions

File tree

include/linux/bpf.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,10 +909,14 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size)
909909
aux->ctx_field_size = size;
910910
}
911911

912+
static bool bpf_is_ldimm64(const struct bpf_insn *insn)
913+
{
914+
return insn->code == (BPF_LD | BPF_IMM | BPF_DW);
915+
}
916+
912917
static inline bool bpf_pseudo_func(const struct bpf_insn *insn)
913918
{
914-
return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
915-
insn->src_reg == BPF_PSEUDO_FUNC;
919+
return bpf_is_ldimm64(insn) && insn->src_reg == BPF_PSEUDO_FUNC;
916920
}
917921

918922
struct bpf_prog_ops {

kernel/bpf/verifier.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15439,15 +15439,16 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
1543915439
struct bpf_verifier_env *env,
1544015440
bool visit_callee)
1544115441
{
15442-
int ret;
15442+
int ret, insn_sz;
1544315443

15444-
ret = push_insn(t, t + 1, FALLTHROUGH, env, false);
15444+
insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1;
15445+
ret = push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
1544515446
if (ret)
1544615447
return ret;
1544715448

15448-
mark_prune_point(env, t + 1);
15449+
mark_prune_point(env, t + insn_sz);
1544915450
/* when we exit from subprog, we need to record non-linear history */
15450-
mark_jmp_point(env, t + 1);
15451+
mark_jmp_point(env, t + insn_sz);
1545115452

1545215453
if (visit_callee) {
1545315454
mark_prune_point(env, t);
@@ -15469,15 +15470,17 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
1546915470
static int visit_insn(int t, struct bpf_verifier_env *env)
1547015471
{
1547115472
struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];
15472-
int ret, off;
15473+
int ret, off, insn_sz;
1547315474

1547415475
if (bpf_pseudo_func(insn))
1547515476
return visit_func_call_insn(t, insns, env, true);
1547615477

1547715478
/* All non-branch instructions have a single fall-through edge. */
1547815479
if (BPF_CLASS(insn->code) != BPF_JMP &&
15479-
BPF_CLASS(insn->code) != BPF_JMP32)
15480-
return push_insn(t, t + 1, FALLTHROUGH, env, false);
15480+
BPF_CLASS(insn->code) != BPF_JMP32) {
15481+
insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
15482+
return push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
15483+
}
1548115484

1548215485
switch (BPF_OP(insn->code)) {
1548315486
case BPF_EXIT:
@@ -15607,11 +15610,21 @@ static int check_cfg(struct bpf_verifier_env *env)
1560715610
}
1560815611

1560915612
for (i = 0; i < insn_cnt; i++) {
15613+
struct bpf_insn *insn = &env->prog->insnsi[i];
15614+
1561015615
if (insn_state[i] != EXPLORED) {
1561115616
verbose(env, "unreachable insn %d\n", i);
1561215617
ret = -EINVAL;
1561315618
goto err_free;
1561415619
}
15620+
if (bpf_is_ldimm64(insn)) {
15621+
if (insn_state[i + 1] != 0) {
15622+
verbose(env, "jump into the middle of ldimm64 insn %d\n", i);
15623+
ret = -EINVAL;
15624+
goto err_free;
15625+
}
15626+
i++; /* skip second half of ldimm64 */
15627+
}
1561515628
}
1561615629
ret = 0; /* cfg looks good */
1561715630

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
BPF_MOV64_IMM(BPF_REG_0, 2),
1010
BPF_EXIT_INSN(),
1111
},
12-
.errstr = "invalid BPF_LD_IMM insn",
13-
.errstr_unpriv = "R1 pointer comparison",
12+
.errstr = "jump into the middle of ldimm64 insn 1",
13+
.errstr_unpriv = "jump into the middle of ldimm64 insn 1",
1414
.result = REJECT,
1515
},
1616
{
@@ -23,8 +23,8 @@
2323
BPF_LD_IMM64(BPF_REG_0, 1),
2424
BPF_EXIT_INSN(),
2525
},
26-
.errstr = "invalid BPF_LD_IMM insn",
27-
.errstr_unpriv = "R1 pointer comparison",
26+
.errstr = "jump into the middle of ldimm64 insn 1",
27+
.errstr_unpriv = "jump into the middle of ldimm64 insn 1",
2828
.result = REJECT,
2929
},
3030
{

0 commit comments

Comments
 (0)