Commit 92974cef authored by Luis Gerhorst's avatar Luis Gerhorst Committed by Alexei Starovoitov
Browse files

selftests/bpf: Add Spectre v4 tests

Add the following tests:

1. A test with an (unimportant) ldimm64 (16 byte insn) and a
   Spectre-v4--induced nospec that clarifies and serves as a basic
   Spectre v4 test.

2. Make sure a Spectre v4 nospec_result does not prevent a Spectre v1
   nospec from being added before the dangerous instruction (tests that
   [1] is fixed).

3. Combine the two, which is the combination that triggers the warning
   in [2]. This is because the unanalyzed stack write has nospec_result
   set, but the ldimm64 (which was just analyzed) had incremented
   insn_idx by 2. That violates the assertion that nospec_result is only
   used after insns that increment insn_idx by 1 (i.e., stack writes).

[1] https://lore.kernel.org/bpf/4266fd5de04092aa4971cbef14f1b4b96961f432.camel@gmail.com/
[2] https://lore.kernel.org/bpf/685b3c1b.050a0220.2303ee.0010.GAE@google.com/



Signed-off-by: default avatarLuis Gerhorst <luis.gerhorst@fau.de>
Link: https://lore.kernel.org/r/20250705190908.1756862-3-luis.gerhorst@fau.de


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent dadb5910
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -237,4 +237,8 @@
#define SPEC_V1
#endif

#if defined(__TARGET_ARCH_x86)
#define SPEC_V4
#endif

#endif
+149 −0
Original line number Diff line number Diff line
@@ -801,4 +801,153 @@ l2_%=: \
	: __clobber_all);
}

SEC("socket")
__description("unpriv: ldimm64 before Spectre v4 barrier")
__success __success_unpriv
__retval(0)
#ifdef SPEC_V4
__xlated_unpriv("r1 = 0x2020200005642020") /* should not matter */
__xlated_unpriv("*(u64 *)(r10 -8) = r1")
__xlated_unpriv("nospec")
#endif
__naked void unpriv_ldimm64_spectre_v4(void)
{
	asm volatile ("					\
	r1 = 0x2020200005642020 ll;			\
	*(u64 *)(r10 -8) = r1;				\
	r0 = 0;						\
	exit;						\
"	::: __clobber_all);
}

SEC("socket")
__description("unpriv: Spectre v1 and v4 barrier")
__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 dangerous `*(u64 *)(NOT_FP -64)
 * = r1` iff `if r9 == 0 goto pc+4` was mispredicted because of Spectre v1. The
 * test therefore ensures the Spectre-v4--induced nospec does not prevent the
 * Spectre-v1--induced speculative path from being fully analyzed.
 */
__xlated_unpriv("nospec") /* Spectre v1 */
__xlated_unpriv("*(u64 *)(r2 -64) = r1") /* could be used to leak r2 */
__xlated_unpriv("nospec") /* Spectre v4 */
#endif
#endif
__naked void unpriv_spectre_v1_and_v4(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("unpriv: Spectre v1 and v4 barrier (simple)")
__success __success_unpriv
__retval(0)
#ifdef SPEC_V1
#ifdef SPEC_V4
__xlated_unpriv("if r8 != 0x0 goto pc+1")
__xlated_unpriv("goto pc+2")
__xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */
__xlated_unpriv("goto pc-1") /* r2 = r0 */
__xlated_unpriv("nospec")
__xlated_unpriv("*(u64 *)(r2 -64) = r1")
__xlated_unpriv("nospec")
#endif
#endif
__naked void unpriv_spectre_v1_and_v4_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);
}

SEC("socket")
__description("unpriv: ldimm64 before Spectre v1 and v4 barrier (simple)")
__success __success_unpriv
__retval(0)
#ifdef SPEC_V1
#ifdef SPEC_V4
__xlated_unpriv("if r8 != 0x0 goto pc+1")
__xlated_unpriv("goto pc+4")
__xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */
__xlated_unpriv("goto pc-1") /* r2 = r0 */
__xlated_unpriv("goto pc-1") /* r1 = 0x2020200005642020 ll */
__xlated_unpriv("goto pc-1") /* second part of ldimm64 */
__xlated_unpriv("nospec")
__xlated_unpriv("*(u64 *)(r2 -64) = r1")
__xlated_unpriv("nospec")
#endif
#endif
__naked void unpriv_ldimm64_spectre_v1_and_v4_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;					\
	r1 = 0x2020200005642020 ll;			\
l2_%=:	*(u64 *)(r2 -64) = r1;				\
l3_%=:	r0 = 0;						\
	exit;						\
"	::: __clobber_all);
}

char _license[] SEC("license") = "GPL";