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
15439 struct bpf_verifier_env *env,15439 struct bpf_verifier_env *env,
15440 bool visit_callee)15440 bool visit_callee)
15441{15441{
15442 int ret;15442 int ret, insn_sz;
15443 15443
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);
15445 if (ret)15446 if (ret)
15446 return ret;15447 return ret;
15447 15448
15448 mark_prune_point(env, t + 1);15449 mark_prune_point(env, t + insn_sz);
15449 /* when we exit from subprog, we need to record non-linear history */15450 /* 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);
15451 15452
15452 if (visit_callee) {15453 if (visit_callee) {
15453 mark_prune_point(env, t);15454 mark_prune_point(env, t);
@@ -15469,15 +15470,17 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns, · 15 unmodified lines
15469static int visit_insn(int t, struct bpf_verifier_env *env)15470static int visit_insn(int t, struct bpf_verifier_env *env)
15470{15471{
15471 struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];15472 struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];
15472 int ret, off;15473 int ret, off, insn_sz;
15473 15474
15474 if (bpf_pseudo_func(insn))15475 if (bpf_pseudo_func(insn))
15475 return visit_func_call_insn(t, insns, env, true);15476 return visit_func_call_insn(t, insns, env, true);
15476 15477
15477 /* All non-branch instructions have a single fall-through edge. */15478 /* All non-branch instructions have a single fall-through edge. */
15478 if (BPF_CLASS(insn->code) != BPF_JMP &&15479 if (BPF_CLASS(insn->code) != BPF_JMP &&
15479 BPF_CLASS(insn->code) != BPF_JMP32)15480 BPF_CLASS(insn->code) != BPF_JMP32) {
15480 return push_insn(t, t + 1, FALLTHROUGH, env, false);15481 insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
15482 return push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
15483 }
15481 15484
15482 switch (BPF_OP(insn->code)) {15485 switch (BPF_OP(insn->code)) {
15483 case BPF_EXIT:15486 case BPF_EXIT:
@@ -15607,11 +15610,21 @@ walk_cfg: · 123 unmodified lines
15607 }15610 }
15608 15611
15609 for (i = 0; i < insn_cnt; i++) {15612 for (i = 0; i < insn_cnt; i++) {
15613 struct bpf_insn *insn = &env->prog->insnsi[i];
15614
15610 if (insn_state[i] != EXPLORED) {15615 if (insn_state[i] != EXPLORED) {
15611 verbose(env, "unreachable insn %d\n", i);15616 verbose(env, "unreachable insn %d\n", i);
15612 ret = -EINVAL;15617 ret = -EINVAL;
15613 goto err_free;15618 goto err_free;
15614 }15619 }
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 }
15615 }15628 }
15616 ret = 0; /* cfg looks good */15629 ret = 0; /* cfg looks good */
15617 15630