* [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path
@ 2025-06-25 18:01 Paul Chaignon
2025-06-25 20:19 ` Eduard Zingerman
0 siblings, 1 reply; 11+ messages in thread
From: Paul Chaignon @ 2025-06-25 18:01 UTC (permalink / raw)
To: bpf; +Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Luis Gerhorst
Commit d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") added a
WARN_ON_ONCE to check that we're not skipping a nospec due to a jump.
It however failed to take into account LDIMM64 instructions as below:
15: (18) r1 = 0x2020200005642020
17: (7b) *(u64 *)(r10 -264) = r1
This bytecode snippet generates a warning because the move from the
LDIMM64 instruction to the next instruction is seen as a jump. This
patch fixes it.
Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com
Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1")
Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
---
kernel/bpf/verifier.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 279a64933262..66841ed6dfc0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -19819,6 +19819,7 @@ static int do_check(struct bpf_verifier_env *env)
int insn_cnt = env->prog->len;
bool do_print_state = false;
int prev_insn_idx = -1;
+ int insn_sz;
for (;;) {
struct bpf_insn *insn;
@@ -19942,7 +19943,8 @@ static int do_check(struct bpf_verifier_env *env)
* to document this in case nospec_result is used
* elsewhere in the future.
*/
- WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1);
+ insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
+ WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz);
process_bpf_exit:
mark_verifier_state_scratched(env);
err = update_branch_counts(env, env->cur_state);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path 2025-06-25 18:01 [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path Paul Chaignon @ 2025-06-25 20:19 ` Eduard Zingerman 2025-06-25 21:18 ` Luis Gerhorst 2025-06-25 21:43 ` Paul Chaignon 0 siblings, 2 replies; 11+ messages in thread From: Eduard Zingerman @ 2025-06-25 20:19 UTC (permalink / raw) To: Paul Chaignon, bpf Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Luis Gerhorst On Wed, 2025-06-25 at 20:01 +0200, Paul Chaignon wrote: > Commit d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") added a > WARN_ON_ONCE to check that we're not skipping a nospec due to a jump. > It however failed to take into account LDIMM64 instructions as below: > > 15: (18) r1 = 0x2020200005642020 > 17: (7b) *(u64 *)(r10 -264) = r1 > > This bytecode snippet generates a warning because the move from the > LDIMM64 instruction to the next instruction is seen as a jump. This > patch fixes it. > > Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com > Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") > Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> > --- > kernel/bpf/verifier.c | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 279a64933262..66841ed6dfc0 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -19819,6 +19819,7 @@ static int do_check(struct bpf_verifier_env *env) > int insn_cnt = env->prog->len; > bool do_print_state = false; > int prev_insn_idx = -1; > + int insn_sz; > > for (;;) { > struct bpf_insn *insn; > @@ -19942,7 +19943,8 @@ static int do_check(struct bpf_verifier_env *env) > * to document this in case nospec_result is used > * elsewhere in the future. > */ > - WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); > + insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; > + WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz); Could you please elaborate a bit? The code looks as follows: prev_insn_idx = env->insn_idx; ... err = do_check_insn(env, do_print_state: &do_print_state); ... if (state->speculative && cur_aux(env)->nospec_result) { ... insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz); ... } The `cur_aux(env)->nospec_result` is set to true only for ST/STX instructions which are 8-bytes wide. `do_check_insn` moves env->isns_idx by 1 for these instructions. So, suppose there is a program: 15: (18) r1 = 0x2020200005642020 17: (7b) *(u64 *)(r10 -264) = r1 Insn processing sequence would look like (starting from 15): - prev_insn_idx <- 15 - do_check_insn() - env->insn_idx <- 17 - prev_insn_idx <- 17 - do_check_insn(): - nospec_result <- true - env->insn_idx <- 18 - state->speculative && cur_aux(env)->nospec_result == true: - WARN_ON_ONCE(18 != 17 + 1) // no warning What do I miss? Could you please add a test case? > process_bpf_exit: > mark_verifier_state_scratched(env); > err = update_branch_counts(env, env->cur_state); ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path 2025-06-25 20:19 ` Eduard Zingerman @ 2025-06-25 21:18 ` Luis Gerhorst 2025-06-25 21:43 ` Paul Chaignon 1 sibling, 0 replies; 11+ messages in thread From: Luis Gerhorst @ 2025-06-25 21:18 UTC (permalink / raw) To: Eduard Zingerman Cc: Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Eduard Zingerman <eddyz87@gmail.com> writes: > On Wed, 2025-06-25 at 20:01 +0200, Paul Chaignon wrote: >> Commit d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") added a >> WARN_ON_ONCE to check that we're not skipping a nospec due to a jump. >> It however failed to take into account LDIMM64 instructions as below: >> >> 15: (18) r1 = 0x2020200005642020 >> 17: (7b) *(u64 *)(r10 -264) = r1 >> >> This bytecode snippet generates a warning because the move from the >> LDIMM64 instruction to the next instruction is seen as a jump. This >> patch fixes it. >> >> Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com >> Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") >> Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> >> --- >> kernel/bpf/verifier.c | 4 +++- >> 1 file changed, 3 insertions(+), 1 deletion(-) >> >> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c >> index 279a64933262..66841ed6dfc0 100644 >> --- a/kernel/bpf/verifier.c >> +++ b/kernel/bpf/verifier.c >> @@ -19819,6 +19819,7 @@ static int do_check(struct bpf_verifier_env *env) >> int insn_cnt = env->prog->len; >> bool do_print_state = false; >> int prev_insn_idx = -1; >> + int insn_sz; >> >> for (;;) { >> struct bpf_insn *insn; >> @@ -19942,7 +19943,8 @@ static int do_check(struct bpf_verifier_env *env) >> * to document this in case nospec_result is used >> * elsewhere in the future. >> */ >> - WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); >> + insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; >> + WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz); > > Could you please elaborate a bit? > The code looks as follows: > > prev_insn_idx = env->insn_idx; > ... > err = do_check_insn(env, do_print_state: &do_print_state); > ... > if (state->speculative && cur_aux(env)->nospec_result) { > ... > insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; > WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz); > ... > } > > The `cur_aux(env)->nospec_result` is set to true only for ST/STX > instructions which are 8-bytes wide. `do_check_insn` moves > env->isns_idx by 1 for these instructions. > > So, suppose there is a program: > > 15: (18) r1 = 0x2020200005642020 > 17: (7b) *(u64 *)(r10 -264) = r1 > > Insn processing sequence would look like (starting from 15): > - prev_insn_idx <- 15 > - do_check_insn() > - env->insn_idx <- 17 > - prev_insn_idx <- 17 > - do_check_insn(): > - nospec_result <- true > - env->insn_idx <- 18 > - state->speculative && cur_aux(env)->nospec_result == true: > - WARN_ON_ONCE(18 != 17 + 1) // no warning > > What do I miss? > Could you please add a test case? Thanks for looking into it. Yes, ldimm64 should not require a nospec_result as it can not be subject to SSB. Should be as in https://github.com/kernel-patches/bpf/pull/9193/files (ignore the arm failures, these are because of the BUG in the test) I will continue looking into it tomorrow if you don't want to. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path 2025-06-25 20:19 ` Eduard Zingerman 2025-06-25 21:18 ` Luis Gerhorst @ 2025-06-25 21:43 ` Paul Chaignon 2025-06-25 22:13 ` Eduard Zingerman 1 sibling, 1 reply; 11+ messages in thread From: Paul Chaignon @ 2025-06-25 21:43 UTC (permalink / raw) To: Eduard Zingerman Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Luis Gerhorst Thanks for the review! On Wed, Jun 25, 2025 at 01:19:01PM -0700, Eduard Zingerman wrote: > On Wed, 2025-06-25 at 20:01 +0200, Paul Chaignon wrote: > > Commit d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") added a > > WARN_ON_ONCE to check that we're not skipping a nospec due to a jump. > > It however failed to take into account LDIMM64 instructions as below: > > > > 15: (18) r1 = 0x2020200005642020 > > 17: (7b) *(u64 *)(r10 -264) = r1 > > > > This bytecode snippet generates a warning because the move from the > > LDIMM64 instruction to the next instruction is seen as a jump. This > > patch fixes it. > > > > Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com > > Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") > > Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> > > --- > > kernel/bpf/verifier.c | 4 +++- > > 1 file changed, 3 insertions(+), 1 deletion(-) > > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > > index 279a64933262..66841ed6dfc0 100644 > > --- a/kernel/bpf/verifier.c > > +++ b/kernel/bpf/verifier.c > > @@ -19819,6 +19819,7 @@ static int do_check(struct bpf_verifier_env *env) > > int insn_cnt = env->prog->len; > > bool do_print_state = false; > > int prev_insn_idx = -1; > > + int insn_sz; > > > > for (;;) { > > struct bpf_insn *insn; > > @@ -19942,7 +19943,8 @@ static int do_check(struct bpf_verifier_env *env) > > * to document this in case nospec_result is used > > * elsewhere in the future. > > */ > > - WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); > > + insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; > > + WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz); > > Could you please elaborate a bit? > The code looks as follows: > > prev_insn_idx = env->insn_idx; > ... > err = do_check_insn(env, do_print_state: &do_print_state); > ... > if (state->speculative && cur_aux(env)->nospec_result) { > ... > insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; > WARN_ON_ONCE(env->insn_idx != prev_insn_idx + insn_sz); > ... > } > > The `cur_aux(env)->nospec_result` is set to true only for ST/STX > instructions which are 8-bytes wide. `do_check_insn` moves > env->isns_idx by 1 for these instructions. > > So, suppose there is a program: > > 15: (18) r1 = 0x2020200005642020 > 17: (7b) *(u64 *)(r10 -264) = r1 > > Insn processing sequence would look like (starting from 15): > - prev_insn_idx <- 15 > - do_check_insn() > - env->insn_idx <- 17 > - prev_insn_idx <- 17 > - do_check_insn(): > - nospec_result <- true > - env->insn_idx <- 18 > - state->speculative && cur_aux(env)->nospec_result == true: > - WARN_ON_ONCE(18 != 17 + 1) // no warning > > What do I miss? In the if condition, "cur_aux(env)" points to the aux data of the next instruction (#17 here) because we incremented "insn_idx" in do_check_insn(). In my fix, "insn" points to the previous instruction because we retrieved it before calling do_check_insn(). Therefore, the processing sequence would look like: - prev_insn_idx <- 15 - do_check_insn() - env->insn_idx <- 17 - state->speculative && cur_aux(env)->nospec_result == true: - WARN_ON_ONCE(17 != 15 + 1) // warning I added a verbose() and recompiled to confirm those numbers. If that makes sense, I'll send a v2 with: - A better description, probably with a walkthrough. - A test case simplified from the syzkaller repro. - insn_sz renamed to prev_insn_sz for clarity. > Could you please add a test case? > > > process_bpf_exit: > > mark_verifier_state_scratched(env); > > err = update_branch_counts(env, env->cur_state); ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path 2025-06-25 21:43 ` Paul Chaignon @ 2025-06-25 22:13 ` Eduard Zingerman 2025-06-26 12:45 ` Luis Gerhorst 0 siblings, 1 reply; 11+ messages in thread From: Eduard Zingerman @ 2025-06-25 22:13 UTC (permalink / raw) To: Paul Chaignon Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Luis Gerhorst On Wed, 2025-06-25 at 23:43 +0200, Paul Chaignon wrote: [...] > > So, suppose there is a program: > > > > 15: (18) r1 = 0x2020200005642020 > > 17: (7b) *(u64 *)(r10 -264) = r1 > > > > Insn processing sequence would look like (starting from 15): > > - prev_insn_idx <- 15 > > - do_check_insn() > > - env->insn_idx <- 17 > > - prev_insn_idx <- 17 > > - do_check_insn(): > > - nospec_result <- true > > - env->insn_idx <- 18 > > - state->speculative && cur_aux(env)->nospec_result == true: > > - WARN_ON_ONCE(18 != 17 + 1) // no warning > > > > What do I miss? > > In the if condition, "cur_aux(env)" points to the aux data of the next > instruction (#17 here) because we incremented "insn_idx" in > do_check_insn(). In my fix, "insn" points to the previous instruction > because we retrieved it before calling do_check_insn(). > > Therefore, the processing sequence would look like: > - prev_insn_idx <- 15 > - do_check_insn() > - env->insn_idx <- 17 > - state->speculative && cur_aux(env)->nospec_result == true: > - WARN_ON_ONCE(17 != 15 + 1) // warning I'm sorry, I'm a bit slow today (well, maybe not only today). Isn't it a slightly different bug in the original check? Suppose current insn is ST/STX that do_check_insn() marked as nospec_result. I think the intent was to stop branch processing right at that point, as nospec instruction would be inserted after this store => no need to speculate further. In other words, cur_aux(env)->nospec_result pointing to instruction after ST/STX was not anticipated. (Luis, wdyt?) > I added a verbose() and recompiled to confirm those numbers. > > If that makes sense, I'll send a v2 with: > - A better description, probably with a walkthrough. > - A test case simplified from the syzkaller repro. > - insn_sz renamed to prev_insn_sz for clarity. [...] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path 2025-06-25 22:13 ` Eduard Zingerman @ 2025-06-26 12:45 ` Luis Gerhorst 2025-06-26 12:49 ` [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() Luis Gerhorst ` (2 more replies) 0 siblings, 3 replies; 11+ messages in thread From: Luis Gerhorst @ 2025-06-26 12:45 UTC (permalink / raw) To: Eduard Zingerman Cc: Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Eduard Zingerman <eddyz87@gmail.com> writes: > On Wed, 2025-06-25 at 23:43 +0200, Paul Chaignon wrote: > > [...] > >> > So, suppose there is a program: >> > >> > 15: (18) r1 = 0x2020200005642020 >> > 17: (7b) *(u64 *)(r10 -264) = r1 >> > >> > Insn processing sequence would look like (starting from 15): >> > - prev_insn_idx <- 15 >> > - do_check_insn() >> > - env->insn_idx <- 17 >> > - prev_insn_idx <- 17 >> > - do_check_insn(): >> > - nospec_result <- true >> > - env->insn_idx <- 18 >> > - state->speculative && cur_aux(env)->nospec_result == true: >> > - WARN_ON_ONCE(18 != 17 + 1) // no warning >> > >> > What do I miss? >> >> In the if condition, "cur_aux(env)" points to the aux data of the next >> instruction (#17 here) because we incremented "insn_idx" in >> do_check_insn(). In my fix, "insn" points to the previous instruction >> because we retrieved it before calling do_check_insn(). >> >> Therefore, the processing sequence would look like: >> - prev_insn_idx <- 15 >> - do_check_insn() >> - env->insn_idx <- 17 >> - state->speculative && cur_aux(env)->nospec_result == true: >> - WARN_ON_ONCE(17 != 15 + 1) // warning > > I'm sorry, I'm a bit slow today (well, maybe not only today). > Isn't it a slightly different bug in the original check? > Suppose current insn is ST/STX that do_check_insn() marked as > nospec_result. I think the intent was to stop branch processing right > at that point, as nospec instruction would be inserted after this > store => no need to speculate further. > In other words, cur_aux(env)->nospec_result pointing to instruction > after ST/STX was not anticipated. (Luis, wdyt?) That's a very good point, nospec_result should only stop the path analysis after the insn that has it set was analyzed. Otherwise, a nospec required before the insn may not be added. In reply to this you find a RFC fix and test that shows a nospec might be missing. If this makes sense I will send a polished version. The tests fail without the fix [1] (the offset no longer matches because the nospec before the stack-write is missing, which is the main point), but succeed otherwise [2]. I manually verified the fix resolves the warning in the reproducer, but I can add a test for the polished version. [1] https://github.com/kernel-patches/bpf/actions/runs/15901586938/job/44846011518?pr=9199#step:5:11308 [2] https://github.com/kernel-patches/bpf/pull/9198 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() 2025-06-26 12:45 ` Luis Gerhorst @ 2025-06-26 12:49 ` Luis Gerhorst 2025-06-26 18:40 ` Eduard Zingerman 2025-06-26 18:41 ` Alexei Starovoitov 2025-06-26 13:00 ` [RFC PATCH 2/3] selftests/bpf: Add ldimm64 nospec test Luis Gerhorst 2025-06-26 13:01 ` [RFC PATCH 3/3] selftests/bpf: Add nospec_result test Luis Gerhorst 2 siblings, 2 replies; 11+ messages in thread From: Luis Gerhorst @ 2025-06-26 12:49 UTC (permalink / raw) To: Eduard Zingerman, Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Luis Gerhorst, syzbot+dc27c5fb8388e38d2d37 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(). Reported-by: Paul Chaignon <paul.chaignon@gmail.com> Reported-by: Eduard Zingerman <eddyz87@gmail.com> Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de> --- kernel/bpf/verifier.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f403524bd215..88613fb71b16 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19955,11 +19955,11 @@ static int do_check(struct bpf_verifier_env *env) /* Prevent this speculative path from ever reaching the * insn that would have been unsafe to execute. */ - cur_aux(env)->nospec = true; + env->insn_aux_data[prev_insn_idx].nospec = true; /* If it was an ADD/SUB insn, potentially remove any * markings for alu sanitization. */ - cur_aux(env)->alu_state = 0; + env->insn_aux_data[prev_insn_idx].alu_state = 0; goto process_bpf_exit; } else if (err < 0) { return err; @@ -19968,7 +19968,7 @@ static int do_check(struct bpf_verifier_env *env) } WARN_ON_ONCE(err); - if (state->speculative && cur_aux(env)->nospec_result) { + if (state->speculative && env->insn_aux_data[prev_insn_idx].nospec_result) { /* If we are on a path that performed a jump-op, this * may skip a nospec patched-in after the jump. This can * currently never happen because nospec_result is only @@ -19977,6 +19977,8 @@ static int do_check(struct bpf_verifier_env *env) * never skip the following insn. Still, add a warning * to document this in case nospec_result is used * elsewhere in the future. + * + * Therefore, no special case for ldimm64/call required. */ WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); process_bpf_exit: -- 2.49.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() 2025-06-26 12:49 ` [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() Luis Gerhorst @ 2025-06-26 18:40 ` Eduard Zingerman 2025-06-26 18:41 ` Alexei Starovoitov 1 sibling, 0 replies; 11+ messages in thread From: Eduard Zingerman @ 2025-06-26 18:40 UTC (permalink / raw) To: Luis Gerhorst, Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: syzbot+dc27c5fb8388e38d2d37 On Thu, 2025-06-26 at 14:49 +0200, Luis Gerhorst wrote: > 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(). > > Reported-by: Paul Chaignon <paul.chaignon@gmail.com> > Reported-by: Eduard Zingerman <eddyz87@gmail.com> > Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com > Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") > Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de> > --- The fix makes sense to me, please remove RFC and submit. Is d6f1c85f2253 a part of a current kernel release? If so, looks like this fix has to be routed through bpf tree. > kernel/bpf/verifier.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index f403524bd215..88613fb71b16 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -19955,11 +19955,11 @@ static int do_check(struct bpf_verifier_env *env) > /* Prevent this speculative path from ever reaching the > * insn that would have been unsafe to execute. > */ > - cur_aux(env)->nospec = true; > + env->insn_aux_data[prev_insn_idx].nospec = true; I'd say it would be more convenient to have a temporary variable of type `struct bpf_insn_aux_data` here. Otherwise `prev_insn_idx` indexing would always be something to stop and think for a moment. > /* If it was an ADD/SUB insn, potentially remove any > * markings for alu sanitization. > */ > - cur_aux(env)->alu_state = 0; > + env->insn_aux_data[prev_insn_idx].alu_state = 0; > goto process_bpf_exit; > } else if (err < 0) { > return err; > @@ -19968,7 +19968,7 @@ static int do_check(struct bpf_verifier_env *env) > } > WARN_ON_ONCE(err); > > - if (state->speculative && cur_aux(env)->nospec_result) { > + if (state->speculative && env->insn_aux_data[prev_insn_idx].nospec_result) { > /* If we are on a path that performed a jump-op, this > * may skip a nospec patched-in after the jump. This can > * currently never happen because nospec_result is only > @@ -19977,6 +19977,8 @@ static int do_check(struct bpf_verifier_env *env) > * never skip the following insn. Still, add a warning > * to document this in case nospec_result is used > * elsewhere in the future. > + * > + * Therefore, no special case for ldimm64/call required. > */ > WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); > process_bpf_exit: Maybe change this to simply check that nospec is not set for instruction of class BPF_JMP? ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() 2025-06-26 12:49 ` [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() Luis Gerhorst 2025-06-26 18:40 ` Eduard Zingerman @ 2025-06-26 18:41 ` Alexei Starovoitov 1 sibling, 0 replies; 11+ messages in thread From: Alexei Starovoitov @ 2025-06-26 18:41 UTC (permalink / raw) To: Luis Gerhorst Cc: Eduard Zingerman, Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, syzbot+dc27c5fb8388e38d2d37 On Thu, Jun 26, 2025 at 5:50 AM Luis Gerhorst <luis.gerhorst@fau.de> wrote: > > 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(). > > Reported-by: Paul Chaignon <paul.chaignon@gmail.com> > Reported-by: Eduard Zingerman <eddyz87@gmail.com> > Reported-by: syzbot+dc27c5fb8388e38d2d37@syzkaller.appspotmail.com > Fixes: d6f1c85f2253 ("bpf: Fall back to nospec for Spectre v1") > Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de> > --- > kernel/bpf/verifier.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index f403524bd215..88613fb71b16 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -19955,11 +19955,11 @@ static int do_check(struct bpf_verifier_env *env) > /* Prevent this speculative path from ever reaching the > * insn that would have been unsafe to execute. > */ > - cur_aux(env)->nospec = true; > + env->insn_aux_data[prev_insn_idx].nospec = true; May be introduce prev_aux() similar to cur_aux() ? In the future don't bother with RFC tag, since CI won't test them. > /* If it was an ADD/SUB insn, potentially remove any > * markings for alu sanitization. > */ > - cur_aux(env)->alu_state = 0; > + env->insn_aux_data[prev_insn_idx].alu_state = 0; > goto process_bpf_exit; > } else if (err < 0) { > return err; > @@ -19968,7 +19968,7 @@ static int do_check(struct bpf_verifier_env *env) > } > WARN_ON_ONCE(err); > > - if (state->speculative && cur_aux(env)->nospec_result) { > + if (state->speculative && env->insn_aux_data[prev_insn_idx].nospec_result) { > /* If we are on a path that performed a jump-op, this > * may skip a nospec patched-in after the jump. This can > * currently never happen because nospec_result is only > @@ -19977,6 +19977,8 @@ static int do_check(struct bpf_verifier_env *env) > * never skip the following insn. Still, add a warning > * to document this in case nospec_result is used > * elsewhere in the future. > + * > + * Therefore, no special case for ldimm64/call required. > */ > WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1); While at it replace with verifier_bug_if(). ^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC PATCH 2/3] selftests/bpf: Add ldimm64 nospec test 2025-06-26 12:45 ` Luis Gerhorst 2025-06-26 12:49 ` [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() Luis Gerhorst @ 2025-06-26 13:00 ` Luis Gerhorst 2025-06-26 13:01 ` [RFC PATCH 3/3] selftests/bpf: Add nospec_result test Luis Gerhorst 2 siblings, 0 replies; 11+ messages in thread From: Luis Gerhorst @ 2025-06-26 13:00 UTC (permalink / raw) To: Eduard Zingerman, Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Luis Gerhorst Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de> --- tools/testing/selftests/bpf/progs/bpf_misc.h | 4 ++++ .../selftests/bpf/progs/verifier_unpriv.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index a678463e972c..be7d9bfa8390 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -235,4 +235,8 @@ #define SPEC_V1 #endif +#if defined(__TARGET_ARCH_x86) +#define SPEC_V4 +#endif + #endif diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c index 4470541b5e71..35d2625e97b8 100644 --- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c +++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c @@ -801,4 +801,23 @@ l2_%=: \ : __clobber_all); } +SEC("socket") +__description("ldimm64 nospec") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V4 +__xlated_unpriv("r1 = 0x2020200005642020") +__xlated_unpriv("*(u64 *)(r10 -8) = r1") +__xlated_unpriv("nospec") +#endif +__naked void ldimm64_nospec(void) +{ + asm volatile (" \ + r1 = 0x2020200005642020 ll; \ + *(u64 *)(r10 -8) = r1; \ + r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; -- 2.49.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC PATCH 3/3] selftests/bpf: Add nospec_result test 2025-06-26 12:45 ` Luis Gerhorst 2025-06-26 12:49 ` [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() Luis Gerhorst 2025-06-26 13:00 ` [RFC PATCH 2/3] selftests/bpf: Add ldimm64 nospec test Luis Gerhorst @ 2025-06-26 13:01 ` Luis Gerhorst 2 siblings, 0 replies; 11+ messages in thread From: Luis Gerhorst @ 2025-06-26 13:01 UTC (permalink / raw) To: Eduard Zingerman, Paul Chaignon, bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Luis Gerhorst Make sure nospec_result does not prevent a nospec from being added before the dangerous insn. Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de> --- .../selftests/bpf/progs/verifier_unpriv.c | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c index 35d2625e97b8..7684b7824f4a 100644 --- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c +++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c @@ -820,4 +820,94 @@ __naked void ldimm64_nospec(void) " ::: __clobber_all); } +SEC("socket") +__description("spec_v4-induced path termination only after insn check") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V1 +#ifdef SPEC_V4 +/* starts with r0 == r8 == r9 == 0 */ +__xlated_unpriv("if r8 != 0x0 goto pc+1") +__xlated_unpriv("goto pc+2") +__xlated_unpriv("if r9 == 0x0 goto pc+4") +__xlated_unpriv("r2 = r0") +/* Following nospec required to prevent following `*(u64 *)(NULL -64) = r1` iff + * `if r9 == 0 goto pc+4` was mispredicted because of Spectre v1 + */ +__xlated_unpriv("nospec") /* Spectre v1 */ +__xlated_unpriv("*(u64 *)(r2 -64) = r1") +__xlated_unpriv("nospec") /* Spectre v4 (nospec_result) */ +#endif +#endif +__naked void v4_nospec_result_terminates_v1_path(void) +{ + asm volatile (" \ + r1 = 0; \ + *(u64*)(r10 - 8) = r1; \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + call %[bpf_map_lookup_elem]; \ + r8 = r0; \ + r2 = r10; \ + r2 += -8; \ + r1 = %[map_hash_8b] ll; \ + call %[bpf_map_lookup_elem]; \ + r9 = r0; \ + r0 = r10; \ + r1 = 0; \ + r2 = r10; \ + if r8 != 0 goto l0_%=; \ + if r9 != 0 goto l0_%=; \ + r0 = 0; \ +l0_%=: if r8 != 0 goto l1_%=; \ + goto l2_%=; \ +l1_%=: if r9 == 0 goto l3_%=; \ + r2 = r0; \ +l2_%=: *(u64 *)(r2 -64) = r1; \ +l3_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_map_lookup_elem), + __imm_addr(map_hash_8b) + : __clobber_all); +} + +SEC("socket") +__description("spec_v4-induced path termination only after insn check (simplified, with dead code)") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V1 +#ifdef SPEC_V4 +/* starts with r0 == r8 == r9 == 0 */ +__xlated_unpriv("if r8 != 0x0 goto pc+1") /* if r9 == 0 goto l3_%= */ +__xlated_unpriv("goto pc+2") /* goto l2_%= */ +__xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */ +__xlated_unpriv("goto pc-1") /* r2 = r0 */ +__xlated_unpriv("nospec") /* Spectre v1 */ +__xlated_unpriv("*(u64 *)(r2 -64) = r1") +__xlated_unpriv("nospec") /* Spectre v4 (nospec_result) */ +#endif +#endif +__naked void v4_nospec_result_terminates_v1_path_simple(void) +{ + asm volatile (" \ + r8 = 0; \ + r9 = 0; \ + r0 = r10; \ + r1 = 0; \ + r2 = r10; \ + if r8 != 0 goto l0_%=; \ + if r9 != 0 goto l0_%=; \ + r0 = 0; \ +l0_%=: if r8 != 0 goto l1_%=; \ + goto l2_%=; \ +l1_%=: if r9 == 0 goto l3_%=; \ + r2 = r0; \ +l2_%=: *(u64 *)(r2 -64) = r1; \ +l3_%=: r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; -- 2.49.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-06-26 18:41 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-06-25 18:01 [PATCH bpf-next] bpf: Fix unwarranted warning on speculative path Paul Chaignon 2025-06-25 20:19 ` Eduard Zingerman 2025-06-25 21:18 ` Luis Gerhorst 2025-06-25 21:43 ` Paul Chaignon 2025-06-25 22:13 ` Eduard Zingerman 2025-06-26 12:45 ` Luis Gerhorst 2025-06-26 12:49 ` [RFC PATCH 1/3] bpf: Fix aux usage after do_check_insn() Luis Gerhorst 2025-06-26 18:40 ` Eduard Zingerman 2025-06-26 18:41 ` Alexei Starovoitov 2025-06-26 13:00 ` [RFC PATCH 2/3] selftests/bpf: Add ldimm64 nospec test Luis Gerhorst 2025-06-26 13:01 ` [RFC PATCH 3/3] selftests/bpf: Add nospec_result test Luis Gerhorst
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox