]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/bpf: Add test for Spectre v1 mitigation
authorLuis Gerhorst <luis.gerhorst@fau.de>
Tue, 3 Jun 2025 21:28:14 +0000 (23:28 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 10 Jun 2025 03:11:10 +0000 (20:11 -0700)
This is based on the gadget from the description of commit 9183671af6db
("bpf: Fix leakage under speculation on mispredicted branches").

Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de>
Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250603212814.338867-1-luis.gerhorst@fau.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/progs/verifier_unpriv.c

index 9bd4aef721407e5facf988728445d1110f8dd6b4..4470541b5e71b423a55efb2f1abf4bedb84159c0 100644 (file)
@@ -744,4 +744,61 @@ l0_%=:     r0 = 0;                                         \
 "      ::: __clobber_all);
 }
 
+SEC("socket")
+__description("unpriv: Spectre v1 path-based type confusion of scalar as stack-ptr")
+__success __success_unpriv __retval(0)
+#ifdef SPEC_V1
+__xlated_unpriv("if r0 != 0x1 goto pc+2")
+/* This nospec prevents the exploit because it forces the mispredicted (not
+ * taken) `if r0 != 0x0 goto l0_%=` to resolve before using r6 as a pointer.
+ * This causes the CPU to realize that `r6 = r9` should have never executed. It
+ * ensures that r6 always contains a readable stack slot ptr when the insn after
+ * the nospec executes.
+ */
+__xlated_unpriv("nospec")
+__xlated_unpriv("r9 = *(u8 *)(r6 +0)")
+#endif
+__naked void unpriv_spec_v1_type_confusion(void)
+{
+       asm volatile ("                                 \
+       r1 = 0;                                         \
+       *(u64*)(r10 - 8) = r1;                          \
+       r2 = r10;                                       \
+       r2 += -8;                                       \
+       r1 = %[map_hash_8b] ll;                         \
+       call %[bpf_map_lookup_elem];                    \
+       if r0 == 0 goto l2_%=;                          \
+       /* r0: pointer to a map array entry */          \
+       r2 = r10;                                       \
+       r2 += -8;                                       \
+       r1 = %[map_hash_8b] ll;                         \
+       /* r1, r2: prepared call args */                \
+       r6 = r10;                                       \
+       r6 += -8;                                       \
+       /* r6: pointer to readable stack slot */        \
+       r9 = 0xffffc900;                                \
+       r9 <<= 32;                                      \
+       /* r9: scalar controlled by attacker */         \
+       r0 = *(u64 *)(r0 + 0); /* cache miss */         \
+       if r0 != 0x0 goto l0_%=;                        \
+       r6 = r9;                                        \
+l0_%=: if r0 != 0x1 goto l1_%=;                        \
+       r9 = *(u8 *)(r6 + 0);                           \
+l1_%=:  /* leak r9 */                                  \
+       r9 &= 1;                                        \
+       r9 <<= 9;                                       \
+       *(u64*)(r10 - 8) = r9;                          \
+       call %[bpf_map_lookup_elem];                    \
+       if r0 == 0 goto l2_%=;                          \
+       /* leak secret into is_cached(map[0|512]): */   \
+       r0 = *(u64 *)(r0 + 0);                          \
+l2_%=:                                                 \
+       r0 = 0;                                         \
+       exit;                                           \
+"      :
+       : __imm(bpf_map_lookup_elem),
+         __imm_addr(map_hash_8b)
+       : __clobber_all);
+}
+
 char _license[] SEC("license") = "GPL";