Skip to content

Commit 06e45d3

Browse files
jpoimboeshiloong
authored andcommitted
x86/speculation: Prepare entry code for Spectre v1 swapgs mitigations
commit 18ec54f upstream. Spectre v1 isn't only about array bounds checks. It can affect any conditional checks. The kernel entry code interrupt, exception, and NMI handlers all have conditional swapgs checks. Those may be problematic in the context of Spectre v1, as kernel code can speculatively run with a user GS. For example: if (coming from user space) swapgs mov %gs:<percpu_offset>, %reg mov (%reg), %reg1 When coming from user space, the CPU can speculatively skip the swapgs, and then do a speculative percpu load using the user GS value. So the user can speculatively force a read of any kernel value. If a gadget exists which uses the percpu value as an address in another load/store, then the contents of the kernel value may become visible via an L1 side channel attack. A similar attack exists when coming from kernel space. The CPU can speculatively do the swapgs, causing the user GS to get used for the rest of the speculative window. The mitigation is similar to a traditional Spectre v1 mitigation, except: a) index masking isn't possible; because the index (percpu offset) isn't user-controlled; and b) an lfence is needed in both the "from user" swapgs path and the "from kernel" non-swapgs path (because of the two attacks described above). The user entry swapgs paths already have SWITCH_TO_KERNEL_CR3, which has a CR3 write when PTI is enabled. Since CR3 writes are serializing, the lfences can be skipped in those cases. On the other hand, the kernel entry swapgs paths don't depend on PTI. To avoid unnecessary lfences for the user entry case, create two separate features for alternative patching: X86_FEATURE_FENCE_SWAPGS_USER X86_FEATURE_FENCE_SWAPGS_KERNEL Use these features in entry code to patch in lfences where needed. The features aren't enabled yet, so there's no functional change. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Dave Hansen <dave.hansen@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Shile Zhang <shile.zhang@linux.alibaba.com> Acked-by: Joseph Qi <joseph.qi@linux.alibaba.com>
1 parent 74f6567 commit 06e45d3

File tree

3 files changed

+37
-3
lines changed

3 files changed

+37
-3
lines changed

arch/x86/entry/calling.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,23 @@ For 32-bit we have the following conventions - kernel is built with
329329

330330
#endif
331331

332+
/*
333+
* Mitigate Spectre v1 for conditional swapgs code paths.
334+
*
335+
* FENCE_SWAPGS_USER_ENTRY is used in the user entry swapgs code path, to
336+
* prevent a speculative swapgs when coming from kernel space.
337+
*
338+
* FENCE_SWAPGS_KERNEL_ENTRY is used in the kernel entry non-swapgs code path,
339+
* to prevent the swapgs from getting speculatively skipped when coming from
340+
* user space.
341+
*/
342+
.macro FENCE_SWAPGS_USER_ENTRY
343+
ALTERNATIVE "", "lfence", X86_FEATURE_FENCE_SWAPGS_USER
344+
.endm
345+
.macro FENCE_SWAPGS_KERNEL_ENTRY
346+
ALTERNATIVE "", "lfence", X86_FEATURE_FENCE_SWAPGS_KERNEL
347+
.endm
348+
332349
#endif /* CONFIG_X86_64 */
333350

334351
/*

arch/x86/entry/entry_64.S

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ ENTRY(interrupt_entry)
570570
testb $3, CS-ORIG_RAX+8(%rsp)
571571
jz 1f
572572
SWAPGS
573-
573+
FENCE_SWAPGS_USER_ENTRY
574574
/*
575575
* Switch to the thread stack. The IRET frame and orig_ax are
576576
* on the stack, as well as the return address. RDI..R12 are
@@ -600,8 +600,10 @@ ENTRY(interrupt_entry)
600600
UNWIND_HINT_FUNC
601601

602602
movq (%rdi), %rdi
603+
jmpq 2f
603604
1:
604-
605+
FENCE_SWAPGS_KERNEL_ENTRY
606+
2:
605607
PUSH_AND_CLEAR_REGS save_ret=1
606608
ENCODE_FRAME_POINTER 8
607609

@@ -1219,6 +1221,13 @@ ENTRY(paranoid_entry)
12191221
*/
12201222
SAVE_AND_SWITCH_TO_KERNEL_CR3 scratch_reg=%rax save_reg=%r14
12211223

1224+
/*
1225+
* The above SAVE_AND_SWITCH_TO_KERNEL_CR3 macro doesn't do an
1226+
* unconditional CR3 write, even in the PTI case. So do an lfence
1227+
* to prevent GS speculation, regardless of whether PTI is enabled.
1228+
*/
1229+
FENCE_SWAPGS_KERNEL_ENTRY
1230+
12221231
ret
12231232
END(paranoid_entry)
12241233

@@ -1269,6 +1278,7 @@ ENTRY(error_entry)
12691278
* from user mode due to an IRET fault.
12701279
*/
12711280
SWAPGS
1281+
FENCE_SWAPGS_USER_ENTRY
12721282
/* We have user CR3. Change to kernel CR3. */
12731283
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
12741284

@@ -1290,6 +1300,8 @@ ENTRY(error_entry)
12901300
CALL_enter_from_user_mode
12911301
ret
12921302

1303+
.Lerror_entry_done_lfence:
1304+
FENCE_SWAPGS_KERNEL_ENTRY
12931305
.Lerror_entry_done:
12941306
TRACE_IRQS_OFF
12951307
ret
@@ -1308,14 +1320,15 @@ ENTRY(error_entry)
13081320
cmpq %rax, RIP+8(%rsp)
13091321
je .Lbstep_iret
13101322
cmpq $.Lgs_change, RIP+8(%rsp)
1311-
jne .Lerror_entry_done
1323+
jne .Lerror_entry_done_lfence
13121324

13131325
/*
13141326
* hack: .Lgs_change can fail with user gsbase. If this happens, fix up
13151327
* gsbase and proceed. We'll fix up the exception and land in
13161328
* .Lgs_change's error handler with kernel gsbase.
13171329
*/
13181330
SWAPGS
1331+
FENCE_SWAPGS_USER_ENTRY
13191332
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
13201333
jmp .Lerror_entry_done
13211334

@@ -1330,6 +1343,7 @@ ENTRY(error_entry)
13301343
* gsbase and CR3. Switch to kernel gsbase and CR3:
13311344
*/
13321345
SWAPGS
1346+
FENCE_SWAPGS_USER_ENTRY
13331347
SWITCH_TO_KERNEL_CR3 scratch_reg=%rax
13341348

13351349
/*
@@ -1421,6 +1435,7 @@ ENTRY(nmi)
14211435

14221436
swapgs
14231437
cld
1438+
FENCE_SWAPGS_USER_ENTRY
14241439
SWITCH_TO_KERNEL_CR3 scratch_reg=%rdx
14251440
movq %rsp, %rdx
14261441
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp

arch/x86/include/asm/cpufeatures.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@
279279
#define X86_FEATURE_CQM_OCCUP_LLC (11*32+ 1) /* LLC occupancy monitoring */
280280
#define X86_FEATURE_CQM_MBM_TOTAL (11*32+ 2) /* LLC Total MBM monitoring */
281281
#define X86_FEATURE_CQM_MBM_LOCAL (11*32+ 3) /* LLC Local MBM monitoring */
282+
#define X86_FEATURE_FENCE_SWAPGS_USER (11*32+ 4) /* "" LFENCE in user entry SWAPGS path */
283+
#define X86_FEATURE_FENCE_SWAPGS_KERNEL (11*32+ 5) /* "" LFENCE in kernel entry SWAPGS path */
282284

283285
/* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */
284286
#define X86_FEATURE_CLZERO (13*32+ 0) /* CLZERO instruction */

0 commit comments

Comments
 (0)