* [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
* [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
* 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
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.