History

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.

Andrii Nakryiko committed 3 years ago

Commit 3feb2631 parent fe69a1b3 files · +30 −13Browse files

Showing the changes to kernel (1 of 3 files). Show all

Unified · Split

kernel/bpf/verifier.c+20−7
@@ -15439,15 +15439,16 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns, · 15438 unmodified lines
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, · 15 unmodified lines
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 @@ walk_cfg: · 123 unmodified lines
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