Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Fix aux usage after do_check_insn()

We must terminate the speculative analysis if the just-analyzed insn had
nospec_result set. Using cur_aux() here is wrong because insn_idx might
have been incremented by do_check_insn(). Therefore, introduce and use
insn_aux variable.

Also change cur_aux(env)->nospec in case do_check_insn() ever manages to
increment insn_idx but still fail.

Change the warning to check the insn class (which prevents it from
triggering for ldimm64, for which nospec_result would not be
problematic) and use verifier_bug_if().

In line with Eduard's suggestion, do not introduce prev_aux() because
that requires one to understand that after do_check_insn() call what was
current became previous. This would at-least require a comment.

Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1")
Reported-by: Paul Chaignon <paul.chaignon@gmail.com>
Reported-by: Eduard Zingerman <eddyz87@gmail.com>
Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com
Link: https://lore.kernel.org/bpf/685b3c1b.050a0220.2303ee.0010.GAE@google.com/
Link: https://lore.kernel.org/bpf/4266fd5de04092aa4971cbef14f1b4b96961f432.camel@gmail.com/
Suggested-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250705190908.1756862-2-luis.gerhorst@fau.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Luis Gerhorst and committed by
Alexei Starovoitov
dadb5910 0f626c98

+14 -5
+14 -5
kernel/bpf/verifier.c
··· 19953 19953 19954 19954 for (;;) { 19955 19955 struct bpf_insn *insn; 19956 + struct bpf_insn_aux_data *insn_aux; 19956 19957 int err; 19957 19958 19958 19959 /* reset current history entry on each new instruction */ ··· 19967 19966 } 19968 19967 19969 19968 insn = &insns[env->insn_idx]; 19969 + insn_aux = &env->insn_aux_data[env->insn_idx]; 19970 19970 19971 19971 if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) { 19972 19972 verbose(env, ··· 20044 20042 /* Reduce verification complexity by stopping speculative path 20045 20043 * verification when a nospec is encountered. 20046 20044 */ 20047 - if (state->speculative && cur_aux(env)->nospec) 20045 + if (state->speculative && insn_aux->nospec) 20048 20046 goto process_bpf_exit; 20049 20047 20050 20048 err = do_check_insn(env, &do_print_state); ··· 20052 20050 /* Prevent this speculative path from ever reaching the 20053 20051 * insn that would have been unsafe to execute. 20054 20052 */ 20055 - cur_aux(env)->nospec = true; 20053 + insn_aux->nospec = true; 20056 20054 /* If it was an ADD/SUB insn, potentially remove any 20057 20055 * markings for alu sanitization. 20058 20056 */ 20059 - cur_aux(env)->alu_state = 0; 20057 + insn_aux->alu_state = 0; 20060 20058 goto process_bpf_exit; 20061 20059 } else if (err < 0) { 20062 20060 return err; ··· 20065 20063 } 20066 20064 WARN_ON_ONCE(err); 20067 20065 20068 - if (state->speculative && cur_aux(env)->nospec_result) { 20066 + if (state->speculative && insn_aux->nospec_result) { 20069 20067 /* If we are on a path that performed a jump-op, this 20070 20068 * may skip a nospec patched-in after the jump. This can 20071 20069 * currently never happen because nospec_result is only ··· 20074 20072 * never skip the following insn. Still, add a warning 20075 20073 * to document this in case nospec_result is used 20076 20074 * elsewhere in the future. 20075 + * 20076 + * All non-branch instructions have a single 20077 + * fall-through edge. For these, nospec_result should 20078 + * already work. 20077 20079 */ 20078 - WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); 20080 + if (verifier_bug_if(BPF_CLASS(insn->code) == BPF_JMP || 20081 + BPF_CLASS(insn->code) == BPF_JMP32, env, 20082 + "speculation barrier after jump instruction may not have the desired effect")) 20083 + return -EFAULT; 20079 20084 process_bpf_exit: 20080 20085 mark_verifier_state_scratched(env); 20081 20086 err = update_branch_counts(env, env->cur_state);