@@ -729,4 +729,61 @@ l0_%=: r0 = 0; \
729729" ::: __clobber_all );
730730}
731731
732+ SEC ("socket" )
733+ __description ("unpriv: Spectre v1 path-based type confusion of scalar as stack-ptr" )
734+ __success __success_unpriv __retval (0 )
735+ #ifdef SPEC_V1
736+ __xlated_unpriv ("if r0 != 0x1 goto pc+2" )
737+ /* This nospec prevents the exploit because it forces the mispredicted (not
738+ * taken) `if r0 != 0x0 goto l0_%=` to resolve before using r6 as a pointer.
739+ * This causes the CPU to realize that `r6 = r9` should have never executed. It
740+ * ensures that r6 always contains a readable stack slot ptr when the insn after
741+ * the nospec executes.
742+ */
743+ __xlated_unpriv ("nospec" )
744+ __xlated_unpriv ("r9 = *(u8 *)(r6 +0)" )
745+ #endif
746+ __naked void unpriv_spec_v1_type_confusion (void )
747+ {
748+ asm volatile (" \
749+ r1 = 0; \
750+ *(u64*)(r10 - 8) = r1; \
751+ r2 = r10; \
752+ r2 += -8; \
753+ r1 = %[map_hash_8b] ll; \
754+ call %[bpf_map_lookup_elem]; \
755+ if r0 == 0 goto l2_%=; \
756+ /* r0: pointer to a map array entry */ \
757+ r2 = r10; \
758+ r2 += -8; \
759+ r1 = %[map_hash_8b] ll; \
760+ /* r1, r2: prepared call args */ \
761+ r6 = r10; \
762+ r6 += -8; \
763+ /* r6: pointer to readable stack slot */ \
764+ r9 = 0xffffc900; \
765+ r9 <<= 32; \
766+ /* r9: scalar controlled by attacker */ \
767+ r0 = *(u64 *)(r0 + 0); /* cache miss */ \
768+ if r0 != 0x0 goto l0_%=; \
769+ r6 = r9; \
770+ l0_%=: if r0 != 0x1 goto l1_%=; \
771+ r9 = *(u8 *)(r6 + 0); \
772+ l1_%=: /* leak r9 */ \
773+ r9 &= 1; \
774+ r9 <<= 9; \
775+ *(u64*)(r10 - 8) = r9; \
776+ call %[bpf_map_lookup_elem]; \
777+ if r0 == 0 goto l2_%=; \
778+ /* leak secret into is_cached(map[0|512]): */ \
779+ r0 = *(u64 *)(r0 + 0); \
780+ l2_%=: \
781+ r0 = 0; \
782+ exit; \
783+ " :
784+ : __imm (bpf_map_lookup_elem ),
785+ __imm_addr (map_hash_8b )
786+ : __clobber_all );
787+ }
788+
732789char _license [] SEC ("license" ) = "GPL" ;
0 commit comments