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.
Showing the changes to kernel (1 of 3 files). Show all
Unified · Split
| @@ -15439,15 +15439,16 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns, · 15438 unmodified lines | ||
| 15439 | 15439 | struct bpf_verifier_env *env, |
| 15440 | 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 | 15446 | if (ret) |
| 15446 | 15447 | return ret; |
| 15447 | 15448 | |
| 15448 | − mark_prune_point(env, t + 1); | |
| 15449 | + mark_prune_point(env, t + insn_sz); | |
| 15449 | 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 | 15453 | if (visit_callee) { |
| 15453 | 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 | ||
| 15469 | 15470 | static int visit_insn(int t, struct bpf_verifier_env *env) |
| 15470 | 15471 | { |
| 15471 | 15472 | struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t]; |
| 15472 | − int ret, off; | |
| 15473 | + int ret, off, insn_sz; | |
| 15473 | 15474 | |
| 15474 | 15475 | if (bpf_pseudo_func(insn)) |
| 15475 | 15476 | return visit_func_call_insn(t, insns, env, true); |
| 15476 | 15477 | |
| 15477 | 15478 | /* All non-branch instructions have a single fall-through edge. */ |
| 15478 | 15479 | 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 | + } | |
| 15481 | 15484 | |
| 15482 | 15485 | switch (BPF_OP(insn->code)) { |
| 15483 | 15486 | case BPF_EXIT: |
| @@ -15607,11 +15610,21 @@ walk_cfg: · 123 unmodified lines | ||
| 15607 | 15610 | } |
| 15608 | 15611 | |
| 15609 | 15612 | for (i = 0; i < insn_cnt; i++) { |
| 15613 | + struct bpf_insn *insn = &env->prog->insnsi[i]; | |
| 15614 | + | |
| 15610 | 15615 | if (insn_state[i] != EXPLORED) { |
| 15611 | 15616 | verbose(env, "unreachable insn %d\n", i); |
| 15612 | 15617 | ret = -EINVAL; |
| 15613 | 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 | 15629 | ret = 0; /* cfg looks good */ |
| 15617 | 15630 | |