Skip to content

Commit 08b8b72

Browse files
committed
[X86] Add inline assembly load hardening mitigation for Load Value Injection (LVI)
Added code to X86AsmParser::emitInstruction() to add an LFENCE after each instruction that may load, and emit a warning if it encounters an instruction that may be vulnerable, but cannot be automatically mitigated. Differential Revision: https://reviews.llvm.org/D76158
1 parent 609ef94 commit 08b8b72

File tree

2 files changed

+262
-0
lines changed

2 files changed

+262
-0
lines changed

llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "llvm/MC/MCStreamer.h"
3232
#include "llvm/MC/MCSubtargetInfo.h"
3333
#include "llvm/MC/MCSymbol.h"
34+
#include "llvm/Support/CommandLine.h"
3435
#include "llvm/Support/SourceMgr.h"
3536
#include "llvm/Support/TargetRegistry.h"
3637
#include "llvm/Support/raw_ostream.h"
@@ -39,6 +40,11 @@
3940

4041
using namespace llvm;
4142

43+
static cl::opt<bool> LVIInlineAsmHardening(
44+
"x86-experimental-lvi-inline-asm-hardening",
45+
cl::desc("Harden inline assembly code that may be vulnerable to Load Value"
46+
" Injection (LVI). This feature is experimental."), cl::Hidden);
47+
4248
static bool checkScale(unsigned Scale, StringRef &ErrMsg) {
4349
if (Scale != 1 && Scale != 2 && Scale != 4 && Scale != 8) {
4450
ErrMsg = "scale factor in address must be 1, 2, 4 or 8";
@@ -930,6 +936,11 @@ class X86AsmParser : public MCTargetAsmParser {
930936
bool validateInstruction(MCInst &Inst, const OperandVector &Ops);
931937
bool processInstruction(MCInst &Inst, const OperandVector &Ops);
932938

939+
// Load Value Injection (LVI) Mitigations for machine code
940+
void emitWarningForSpecialLVIInstruction(SMLoc Loc);
941+
bool applyLVICFIMitigation(MCInst &Inst);
942+
bool applyLVILoadHardeningMitigation(MCInst &Inst, MCStreamer &Out);
943+
933944
/// Wrapper around MCStreamer::emitInstruction(). Possibly adds
934945
/// instrumentation around Inst.
935946
void emitInstruction(MCInst &Inst, OperandVector &Operands, MCStreamer &Out);
@@ -3149,9 +3160,104 @@ bool X86AsmParser::validateInstruction(MCInst &Inst, const OperandVector &Ops) {
31493160

31503161
static const char *getSubtargetFeatureName(uint64_t Val);
31513162

3163+
void X86AsmParser::emitWarningForSpecialLVIInstruction(SMLoc Loc) {
3164+
Warning(Loc, "Instruction may be vulnerable to LVI and "
3165+
"requires manual mitigation");
3166+
Note(SMLoc(), "See https://software.intel.com/"
3167+
"security-software-guidance/insights/"
3168+
"deep-dive-load-value-injection#specialinstructions"
3169+
" for more information");
3170+
}
3171+
3172+
/// RET instructions and also instructions that indirect calls/jumps from memory
3173+
/// combine a load and a branch within a single instruction. To mitigate these
3174+
/// instructions against LVI, they must be decomposed into separate load and
3175+
/// branch instructions, with an LFENCE in between. For more details, see:
3176+
/// - X86LoadValueInjectionRetHardening.cpp
3177+
/// - X86LoadValueInjectionIndirectThunks.cpp
3178+
/// - https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection
3179+
///
3180+
/// Returns `true` if a mitigation was applied or warning was emitted.
3181+
bool X86AsmParser::applyLVICFIMitigation(MCInst &Inst) {
3182+
// Information on control-flow instructions that require manual mitigation can
3183+
// be found here:
3184+
// https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions
3185+
switch (Inst.getOpcode()) {
3186+
case X86::RETW:
3187+
case X86::RETL:
3188+
case X86::RETQ:
3189+
case X86::RETIL:
3190+
case X86::RETIQ:
3191+
case X86::RETIW:
3192+
case X86::JMP16m:
3193+
case X86::JMP32m:
3194+
case X86::JMP64m:
3195+
case X86::CALL16m:
3196+
case X86::CALL32m:
3197+
case X86::CALL64m:
3198+
emitWarningForSpecialLVIInstruction(Inst.getLoc());
3199+
return true;
3200+
}
3201+
return false;
3202+
}
3203+
3204+
/// To mitigate LVI, every instruction that performs a load can be followed by
3205+
/// an LFENCE instruction to squash any potential mis-speculation. There are
3206+
/// some instructions that require additional considerations, and may requre
3207+
/// manual mitigation. For more details, see:
3208+
/// https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection
3209+
///
3210+
/// Returns `true` if a mitigation was applied or warning was emitted.
3211+
bool X86AsmParser::applyLVILoadHardeningMitigation(MCInst &Inst,
3212+
MCStreamer &Out) {
3213+
auto Opcode = Inst.getOpcode();
3214+
auto Flags = Inst.getFlags();
3215+
if ((Flags & X86::IP_HAS_REPEAT) || (Flags & X86::IP_HAS_REPEAT_NE)) {
3216+
// Information on REP string instructions that require manual mitigation can
3217+
// be found here:
3218+
// https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions
3219+
switch (Opcode) {
3220+
case X86::CMPSB:
3221+
case X86::CMPSW:
3222+
case X86::CMPSL:
3223+
case X86::CMPSQ:
3224+
case X86::SCASB:
3225+
case X86::SCASW:
3226+
case X86::SCASL:
3227+
case X86::SCASQ:
3228+
emitWarningForSpecialLVIInstruction(Inst.getLoc());
3229+
return true;
3230+
}
3231+
} else if (Opcode == X86::REP_PREFIX || Opcode == X86::REPNE_PREFIX) {
3232+
// If a REP instruction is found on its own line, it may or may not be
3233+
// followed by a vulnerable instruction. Emit a warning just in case.
3234+
emitWarningForSpecialLVIInstruction(Inst.getLoc());
3235+
return true;
3236+
}
3237+
3238+
const MCInstrDesc &MCID = MII.get(Inst.getOpcode());
3239+
// LFENCE has the mayLoad property, don't double fence.
3240+
if (MCID.mayLoad() && Inst.getOpcode() != X86::LFENCE) {
3241+
MCInst FenceInst;
3242+
FenceInst.setOpcode(X86::LFENCE);
3243+
FenceInst.setLoc(Inst.getLoc());
3244+
Out.emitInstruction(FenceInst, getSTI());
3245+
return true;
3246+
}
3247+
return false;
3248+
}
3249+
31523250
void X86AsmParser::emitInstruction(MCInst &Inst, OperandVector &Operands,
31533251
MCStreamer &Out) {
31543252
Out.emitInstruction(Inst, getSTI());
3253+
3254+
if (LVIInlineAsmHardening) {
3255+
if (getSTI().getFeatureBits()[X86::FeatureLVIControlFlowIntegrity] &&
3256+
applyLVICFIMitigation(Inst))
3257+
return;
3258+
if (getSTI().getFeatureBits()[X86::FeatureLVILoadHardening])
3259+
applyLVILoadHardeningMitigation(Inst, Out);
3260+
}
31553261
}
31563262

31573263
bool X86AsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
; RUN: llc -verify-machineinstrs -mtriple=x86_64-unknown -mattr=+lvi-load-hardening -mattr=+lvi-cfi -x86-experimental-lvi-inline-asm-hardening < %s -o %t.out 2> %t.err
2+
; RUN: FileCheck %s --check-prefix=X86 < %t.out
3+
; RUN: FileCheck %s --check-prefix=WARN < %t.err
4+
5+
; Test module-level assembly
6+
module asm "pop %rbx"
7+
module asm "ret"
8+
; WARN: warning: Instruction may be vulnerable to LVI
9+
; WARN-NEXT: ret
10+
; WARN-NEXT: ^
11+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
12+
13+
; Function Attrs: noinline nounwind optnone uwtable
14+
define dso_local void @test_inline_asm() {
15+
entry:
16+
; X86-LABEL: test_inline_asm:
17+
call void asm sideeffect "mov 0x3fed(%rip),%rax", "~{dirflag},~{fpsr},~{flags}"() #1
18+
; X86: movq 16365(%rip), %rax
19+
; X86-NEXT: lfence
20+
call void asm sideeffect "movdqa 0x0(%rip),%xmm0", "~{dirflag},~{fpsr},~{flags}"() #1
21+
; X86: movdqa (%rip), %xmm0
22+
; X86-NEXT: lfence
23+
call void asm sideeffect "movslq 0x3e5d(%rip),%rbx", "~{dirflag},~{fpsr},~{flags}"() #1
24+
; X86: movslq 15965(%rip), %rbx
25+
; X86-NEXT: lfence
26+
call void asm sideeffect "mov (%r12,%rax,8),%rax", "~{dirflag},~{fpsr},~{flags}"() #1
27+
; X86: movq (%r12,%rax,8), %rax
28+
; X86-NEXT: lfence
29+
call void asm sideeffect "movq (24)(%rsi), %r11", "~{dirflag},~{fpsr},~{flags}"() #1
30+
; X86: movq 24(%rsi), %r11
31+
; X86-NEXT: lfence
32+
call void asm sideeffect "cmove %r12,%rax", "~{dirflag},~{fpsr},~{flags}"() #1
33+
; X86: cmoveq %r12, %rax
34+
; X86-NOT: lfence
35+
call void asm sideeffect "cmove (%r12),%rax", "~{dirflag},~{fpsr},~{flags}"() #1
36+
; X86: cmoveq (%r12), %rax
37+
; X86-NEXT: lfence
38+
call void asm sideeffect "pop %rbx", "~{dirflag},~{fpsr},~{flags}"() #1
39+
; X86: popq %rbx
40+
; X86-NEXT: lfence
41+
call void asm sideeffect "popq %rbx", "~{dirflag},~{fpsr},~{flags}"() #1
42+
; X86: popq %rbx
43+
; X86-NEXT: lfence
44+
call void asm sideeffect "xchg (%r12),%rax", "~{dirflag},~{fpsr},~{flags}"() #1
45+
; X86: xchgq %rax, (%r12)
46+
; X86-NEXT: lfence
47+
call void asm sideeffect "cmpxchg %r12,(%rax)", "~{dirflag},~{fpsr},~{flags}"() #1
48+
; X86: cmpxchgq %r12, (%rax)
49+
; X86-NEXT: lfence
50+
call void asm sideeffect "vpxor (%rcx,%rdx,1),%ymm1,%ymm0", "~{dirflag},~{fpsr},~{flags}"() #1
51+
; X86: vpxor (%rcx,%rdx), %ymm1, %ymm0
52+
; X86-NEXT: lfence
53+
call void asm sideeffect "vpmuludq 0x20(%rsi),%ymm0,%ymm12", "~{dirflag},~{fpsr},~{flags}"() #1
54+
; X86: vpmuludq 32(%rsi), %ymm0, %ymm12
55+
; X86-NEXT: lfence
56+
call void asm sideeffect "vpexpandq 0x40(%rdi),%zmm8{%k2}{z}", "~{dirflag},~{fpsr},~{flags}"() #1
57+
; X86: vpexpandq 64(%rdi), %zmm8 {%k2} {z}
58+
; X86-NEXT: lfence
59+
call void asm sideeffect "addq (%r12),%rax", "~{dirflag},~{fpsr},~{flags}"() #1
60+
; X86: addq (%r12), %rax
61+
; X86-NEXT: lfence
62+
call void asm sideeffect "subq Lpoly+0(%rip), %rax", "~{dirflag},~{fpsr},~{flags}"() #1
63+
; X86: subq Lpoly+0(%rip), %rax
64+
; X86-NEXT: lfence
65+
call void asm sideeffect "adcq %r12,(%rax)", "~{dirflag},~{fpsr},~{flags}"() #1
66+
; X86: adcq %r12, (%rax)
67+
; X86-NEXT: lfence
68+
call void asm sideeffect "negq (%rax)", "~{dirflag},~{fpsr},~{flags}"() #1
69+
; X86: negq (%rax)
70+
; X86-NEXT: lfence
71+
call void asm sideeffect "incq %rax", "~{dirflag},~{fpsr},~{flags}"() #1
72+
; X86: incq %rax
73+
; X86-NOT: lfence
74+
call void asm sideeffect "mulq (%rax)", "~{dirflag},~{fpsr},~{flags}"() #1
75+
; X86: mulq (%rax)
76+
; X86-NEXT: lfence
77+
call void asm sideeffect "imulq (%rax),%rdx", "~{dirflag},~{fpsr},~{flags}"() #1
78+
; X86: imulq (%rax), %rdx
79+
; X86-NEXT: lfence
80+
call void asm sideeffect "shlq $$1,(%rax)", "~{dirflag},~{fpsr},~{flags}"() #1
81+
; X86: shlq (%rax)
82+
; X86-NEXT: lfence
83+
call void asm sideeffect "shrq $$1,(%rax)", "~{dirflag},~{fpsr},~{flags}"() #1
84+
; X86: shrq (%rax)
85+
; X86-NEXT: lfence
86+
call void asm sideeffect "repz cmpsb %es:(%rdi),%ds:(%rsi)", "~{dirflag},~{fpsr},~{flags}"() #1
87+
; WARN: warning: Instruction may be vulnerable to LVI
88+
; WARN-NEXT: repz cmpsb %es:(%rdi),%ds:(%rsi)
89+
; WARN-NEXT: ^
90+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
91+
; X86: rep cmpsb %es:(%rdi), %ds:(%rsi)
92+
; X86-NOT: lfence
93+
call void asm sideeffect "repnz scasb", "~{dirflag},~{fpsr},~{flags}"() #1
94+
; WARN: warning: Instruction may be vulnerable to LVI
95+
; WARN-NEXT: repnz scasb
96+
; WARN-NEXT: ^
97+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
98+
; X86: repne scasb %es:(%rdi), %al
99+
; X86-NOT: lfence
100+
call void asm sideeffect "repnz", ""() #1
101+
; WARN: warning: Instruction may be vulnerable to LVI
102+
; WARN-NEXT: repnz
103+
; WARN-NEXT: ^
104+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
105+
call void asm sideeffect "pinsrw $$0x6,(%eax),%xmm0", "~{dirflag},~{fpsr},~{flags}"() #1
106+
; X86: pinsrw $6, (%eax), %xmm0
107+
; X86-NEXT: lfence
108+
call void asm sideeffect "ret", "~{dirflag},~{fpsr},~{flags}"() #1
109+
; WARN: warning: Instruction may be vulnerable to LVI
110+
; WARN-NEXT: ret
111+
; WARN-NEXT: ^
112+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
113+
; X86: retq
114+
; X86-NOT: lfence
115+
call void asm sideeffect "ret $$8", "~{dirflag},~{fpsr},~{flags}"() #1
116+
; WARN: warning: Instruction may be vulnerable to LVI
117+
; WARN-NEXT: ret $8
118+
; WARN-NEXT: ^
119+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
120+
; X86: retq $8
121+
; X86-NOT: lfence
122+
call void asm sideeffect "jmpq *(%rdx)", "~{dirflag},~{fpsr},~{flags}"() #1
123+
; WARN: warning: Instruction may be vulnerable to LVI
124+
; WARN-NEXT: jmpq *(%rdx)
125+
; WARN-NEXT: ^
126+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
127+
; X86: jmpq *(%rdx)
128+
; X86-NOT: lfence
129+
call void asm sideeffect "jmpq *0x100(%rdx)", "~{dirflag},~{fpsr},~{flags}"() #1
130+
; WARN: warning: Instruction may be vulnerable to LVI
131+
; WARN-NEXT: jmpq *0x100(%rdx)
132+
; WARN-NEXT: ^
133+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
134+
; X86: jmpq *256(%rdx)
135+
; X86-NOT: lfence
136+
call void asm sideeffect "callq *200(%rdx)", "~{dirflag},~{fpsr},~{flags}"() #1
137+
; WARN: warning: Instruction may be vulnerable to LVI
138+
; WARN-NEXT: callq *200(%rdx)
139+
; WARN-NEXT: ^
140+
; WARN-NEXT: note: See https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions for more information
141+
; X86: callq *200(%rdx)
142+
; X86-NOT: lfence
143+
call void asm sideeffect "fldt 0x8(%rbp)", "~{dirflag},~{fpsr},~{flags}"() #1
144+
; X86: fldt 8(%rbp)
145+
; X86-NEXT: lfence
146+
call void asm sideeffect "fld %st(0)", "~{dirflag},~{fpsr},~{flags}"() #1
147+
; X86: fld %st(0)
148+
; X86-NOT: lfence
149+
; Test assembler macros
150+
call void asm sideeffect ".macro mplus1 x\0Aincq (\5Cx)\0A.endm\0Amplus1 %rcx", "~{dirflag},~{fpsr},~{flags}"() #1
151+
; X86: incq (%rcx)
152+
; X86-NEXT: lfence
153+
ret void
154+
}
155+
156+
attributes #1 = { nounwind }

0 commit comments

Comments
 (0)