]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: dont report verifier bug for missing bpf_scc_visit on speculative path
authorEduard Zingerman <eddyz87@gmail.com>
Tue, 16 Sep 2025 21:22:50 +0000 (14:22 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 17 Sep 2025 18:19:58 +0000 (11:19 -0700)
Syzbot generated a program that triggers a verifier_bug() call in
maybe_exit_scc(). maybe_exit_scc() assumes that, when called for a
state with insn_idx in some SCC, there should be an instance of struct
bpf_scc_visit allocated for that SCC. Turns out the assumption does
not hold for speculative execution paths. See example in the next
patch.

maybe_scc_exit() is called from update_branch_counts() for states that
reach branch count of zero, meaning that path exploration for a
particular path is finished. Path exploration can finish in one of
three ways:
a. Verification error is found. In this case, update_branch_counts()
   is called only for non-speculative paths.
b. Top level BPF_EXIT is reached. Such instructions are never a part of
   an SCC, so compute_scc_callchain() in maybe_scc_exit() will return
   false, and maybe_scc_exit() will return early.
c. A checkpoint is reached and matched. Checkpoints are created by
   is_state_visited(), which calls maybe_enter_scc(), which allocates
   bpf_scc_visit instances for checkpoints within SCCs.

Hence, for non-speculative symbolic execution paths, the assumption
still holds: if maybe_scc_exit() is called for a state within an SCC,
bpf_scc_visit instance must exist.

This patch removes the verifier_bug() call for speculative paths.

Fixes: c9e31900b54c ("bpf: propagate read/precision marks over state graph backedges")
Reported-by: syzbot+3afc814e8df1af64b653@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/bpf/68c85acd.050a0220.2ff435.03a4.GAE@google.com/
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250916212251.3490455-1-eddyz87@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index 1029380f84db3e9a3033aed18dcb4fa62da1467e..beaa391e02fb09fc79cbe9210e48b2169f5645b8 100644 (file)
@@ -1950,9 +1950,24 @@ static int maybe_exit_scc(struct bpf_verifier_env *env, struct bpf_verifier_stat
                return 0;
        visit = scc_visit_lookup(env, callchain);
        if (!visit) {
-               verifier_bug(env, "scc exit: no visit info for call chain %s",
-                            format_callchain(env, callchain));
-               return -EFAULT;
+               /*
+                * If path traversal stops inside an SCC, corresponding bpf_scc_visit
+                * must exist for non-speculative paths. For non-speculative paths
+                * traversal stops when:
+                * a. Verification error is found, maybe_exit_scc() is not called.
+                * b. Top level BPF_EXIT is reached. Top level BPF_EXIT is not a member
+                *    of any SCC.
+                * c. A checkpoint is reached and matched. Checkpoints are created by
+                *    is_state_visited(), which calls maybe_enter_scc(), which allocates
+                *    bpf_scc_visit instances for checkpoints within SCCs.
+                * (c) is the only case that can reach this point.
+                */
+               if (!st->speculative) {
+                       verifier_bug(env, "scc exit: no visit info for call chain %s",
+                                    format_callchain(env, callchain));
+                       return -EFAULT;
+               }
+               return 0;
        }
        if (visit->entry_state != st)
                return 0;