Search code examples
windowsassemblyx86masmspectre

Assembly language (MASM) and Spectre: Variant 2 (CVE-2017-5715) Branch Target Injection


A couple of years ago, I wrote and updated our MASM codebase with this macro below to combat Spectre V2.

NOSPEC_JMP MACRO target:REQ
                PUSH            target
                JMP             x86_indirect_thunk
ENDM


NOSPEC_CALL MACRO target:REQ
                LOCAL           nospec_call_start
                LOCAL           nospec_call_end

                JMP             nospec_call_end

                ALIGN           16

nospec_call_start:
                PUSH            target
                JMP             x86_indirect_thunk

                ALIGN           16

nospec_call_end:
                CALL            nospec_call_start
ENDM


.CODE

;; This is a special sequence that prevents the CPU speculating for indirect calls.

                ALIGN           16

x86_indirect_thunk:
                CALL            retpoline_call_target

;; No benefit from aligning the capture_speculation branch target, as it is only potentially speculatively executed.

capture_speculation:
                PAUSE
                JMP             capture_speculation

                ALIGN           16

retpoline_call_target:
                IFDEF WIN64
                LEA             RSP,[RSP+8]
                ELSE
                LEA             ESP,[ESP+4]
                ENDIF

                RET

For example, here's some assembly code with speculation enabled (MST_QSPECTRE=1)

main PROC NEAR C 
                PUSH            ESI
                PUSH            EDI
                PUSH            EBX
                PUSH            EBP
                MOV             EAX,OFFSET MyFun

;; Generated code to Call an indirect pointer without speculation.

IFDEF MST_QSPECTRE

                NOSPEC_CALL     EAX
ELSE
                CALL            EAX
ENDIF

                POP             EBP
                POP             EBX
                POP             EDI
                POP             ESI
                RET            
 main ENDP
 

The disassembly shows how the speculative instructions are inserted

spectre_v2

Question

In 2021, can I safely remove that MASM macro and rely upon CPU microcode updates etc.,... to resolve any Spectre concerns? For C code, looks like M$ and Linux C compilers have resolved it:

  • Microsoft added /Qspectre to their MSVC compiler.
  • GCC has compiler options for mitigating Spectre v2 via -mfunction-return, etc.,...

Thanks.


Solution

  • Those compiler options work by generating special asm, whether it's retpolines or lfence or whatever. When you're writing asm by hand, obviously it's still up to you whether to manually include special asm or not.

    Changes to OSes are the relevant thing for you. The OS, on a CPU with updated microcode, can defend you from other threads by telling the CPU not to allow branch history from past code to influence future code. (The ability to ask it to do this was added in microcode updates, and usually works by just flushing the branch prediction caches).

    Another software thread executing on the other logical core of the same physical core can "attack" your code on most CPUs, because branch predictors are shared. At least in theory; ASLR might make that implausible if both tasks would need to be using the same virtual addresses for their branch targets to prime the predictors.

    So in user-space, I think you only need to defend yourself from Spectre if you're worried about code running in the same thread (e.g. a JIT engine running untrusted code inside a browser or JVM has to defend itself) or on the same physical core.