bpf: Perform CFG walk for exception callback
authorKumar Kartikeya Dwivedi <memxor@gmail.com>
Tue, 12 Sep 2023 23:32:04 +0000 (01:32 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 16 Sep 2023 16:34:21 +0000 (09:34 -0700)
Since exception callbacks are not referenced using bpf_pseudo_func and
bpf_pseudo_call instructions, check_cfg traversal will never explore
instructions of the exception callback. Even after adding the subprog,
the program will then fail with a 'unreachable insn' error.

We thus need to begin walking from the start of the exception callback
again in check_cfg after a complete CFG traversal finishes, so as to
explore the CFG rooted at the exception callback.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20230912233214.1518551-8-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index ec3f22312516b5895550ecfc44581ff22ee1c843..863e4e6c46169a864f482893a6284f3cdfeee736 100644 (file)
@@ -15126,8 +15126,8 @@ static int check_cfg(struct bpf_verifier_env *env)
 {
        int insn_cnt = env->prog->len;
        int *insn_stack, *insn_state;
-       int ret = 0;
-       int i;
+       int ex_insn_beg, i, ret = 0;
+       bool ex_done = false;
 
        insn_state = env->cfg.insn_state = kvcalloc(insn_cnt, sizeof(int), GFP_KERNEL);
        if (!insn_state)
@@ -15143,6 +15143,7 @@ static int check_cfg(struct bpf_verifier_env *env)
        insn_stack[0] = 0; /* 0 is the first instruction */
        env->cfg.cur_stack = 1;
 
+walk_cfg:
        while (env->cfg.cur_stack > 0) {
                int t = insn_stack[env->cfg.cur_stack - 1];
 
@@ -15169,6 +15170,16 @@ static int check_cfg(struct bpf_verifier_env *env)
                goto err_free;
        }
 
+       if (env->exception_callback_subprog && !ex_done) {
+               ex_insn_beg = env->subprog_info[env->exception_callback_subprog].start;
+
+               insn_state[ex_insn_beg] = DISCOVERED;
+               insn_stack[0] = ex_insn_beg;
+               env->cfg.cur_stack = 1;
+               ex_done = true;
+               goto walk_cfg;
+       }
+
        for (i = 0; i < insn_cnt; i++) {
                if (insn_state[i] != EXPLORED) {
                        verbose(env, "unreachable insn %d\n", i);