Skip to content

Commit a205982

Browse files
jpoimboeKAGA-KOKO
authored andcommitted
x86/speculation: Enable Spectre v1 swapgs mitigations
The previous commit added macro calls in the entry code which mitigate the Spectre v1 swapgs issue if the X86_FEATURE_FENCE_SWAPGS_* features are enabled. Enable those features where applicable. The mitigations may be disabled with "nospectre_v1" or "mitigations=off". There are different features which can affect the risk of attack: - When FSGSBASE is enabled, unprivileged users are able to place any value in GS, using the wrgsbase instruction. This means they can write a GS value which points to any value in kernel space, which can be useful with the following gadget in an interrupt/exception/NMI handler: if (coming from user space) swapgs mov %gs:<percpu_offset>, %reg1 // dependent load or store based on the value of %reg // for example: mov %(reg1), %reg2 If an interrupt is coming from user space, and the entry code speculatively skips the swapgs (due to user branch mistraining), it may speculatively execute the GS-based load and a subsequent dependent load or store, exposing the kernel data to an L1 side channel leak. Note that, on Intel, a similar attack exists in the above gadget when coming from kernel space, if the swapgs gets speculatively executed to switch back to the user GS. On AMD, this variant isn't possible because swapgs is serializing with respect to future GS-based accesses. NOTE: The FSGSBASE patch set hasn't been merged yet, so the above case doesn't exist quite yet. - When FSGSBASE is disabled, the issue is mitigated somewhat because unprivileged users must use prctl(ARCH_SET_GS) to set GS, which restricts GS values to user space addresses only. That means the gadget would need an additional step, since the target kernel address needs to be read from user space first. Something like: if (coming from user space) swapgs mov %gs:<percpu_offset>, %reg1 mov (%reg1), %reg2 // dependent load or store based on the value of %reg2 // for example: mov %(reg2), %reg3 It's difficult to audit for this gadget in all the handlers, so while there are no known instances of it, it's entirely possible that it exists somewhere (or could be introduced in the future). Without tooling to analyze all such code paths, consider it vulnerable. Effects of SMAP on the !FSGSBASE case: - If SMAP is enabled, and the CPU reports RDCL_NO (i.e., not susceptible to Meltdown), the kernel is prevented from speculatively reading user space memory, even L1 cached values. This effectively disables the !FSGSBASE attack vector. - If SMAP is enabled, but the CPU *is* susceptible to Meltdown, SMAP still prevents the kernel from speculatively reading user space memory. But it does *not* prevent the kernel from reading the user value from L1, if it has already been cached. This is probably only a small hurdle for an attacker to overcome. Thanks to Dave Hansen for contributing the speculative_smap() function. Thanks to Andrew Cooper for providing the inside scoop on whether swapgs is serializing on AMD. [ tglx: Fixed the USER fence decision and polished the comment as suggested by Dave Hansen ] Signed-off-by: Josh Poimboeuf <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Dave Hansen <[email protected]>
1 parent 18ec54f commit a205982

File tree

2 files changed

+110
-13
lines changed

2 files changed

+110
-13
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2587,7 +2587,7 @@
25872587
expose users to several CPU vulnerabilities.
25882588
Equivalent to: nopti [X86,PPC]
25892589
kpti=0 [ARM64]
2590-
nospectre_v1 [PPC]
2590+
nospectre_v1 [X86,PPC]
25912591
nobp=0 [S390]
25922592
nospectre_v2 [X86,PPC,S390,ARM64]
25932593
spectre_v2_user=off [X86]
@@ -2936,9 +2936,9 @@
29362936
nosmt=force: Force disable SMT, cannot be undone
29372937
via the sysfs control file.
29382938

2939-
nospectre_v1 [PPC] Disable mitigations for Spectre Variant 1 (bounds
2940-
check bypass). With this option data leaks are possible
2941-
in the system.
2939+
nospectre_v1 [X86,PPC] Disable mitigations for Spectre Variant 1
2940+
(bounds check bypass). With this option data leaks are
2941+
possible in the system.
29422942

29432943
nospectre_v2 [X86,PPC_FSL_BOOK3E,ARM64] Disable all mitigations for
29442944
the Spectre variant 2 (indirect branch prediction)

arch/x86/kernel/cpu/bugs.c

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include "cpu.h"
3636

37+
static void __init spectre_v1_select_mitigation(void);
3738
static void __init spectre_v2_select_mitigation(void);
3839
static void __init ssb_select_mitigation(void);
3940
static void __init l1tf_select_mitigation(void);
@@ -98,17 +99,11 @@ void __init check_bugs(void)
9899
if (boot_cpu_has(X86_FEATURE_STIBP))
99100
x86_spec_ctrl_mask |= SPEC_CTRL_STIBP;
100101

101-
/* Select the proper spectre mitigation before patching alternatives */
102+
/* Select the proper CPU mitigations before patching alternatives: */
103+
spectre_v1_select_mitigation();
102104
spectre_v2_select_mitigation();
103-
104-
/*
105-
* Select proper mitigation for any exposure to the Speculative Store
106-
* Bypass vulnerability.
107-
*/
108105
ssb_select_mitigation();
109-
110106
l1tf_select_mitigation();
111-
112107
mds_select_mitigation();
113108

114109
arch_smt_update();
@@ -273,6 +268,108 @@ static int __init mds_cmdline(char *str)
273268
}
274269
early_param("mds", mds_cmdline);
275270

271+
#undef pr_fmt
272+
#define pr_fmt(fmt) "Spectre V1 : " fmt
273+
274+
enum spectre_v1_mitigation {
275+
SPECTRE_V1_MITIGATION_NONE,
276+
SPECTRE_V1_MITIGATION_AUTO,
277+
};
278+
279+
static enum spectre_v1_mitigation spectre_v1_mitigation __ro_after_init =
280+
SPECTRE_V1_MITIGATION_AUTO;
281+
282+
static const char * const spectre_v1_strings[] = {
283+
[SPECTRE_V1_MITIGATION_NONE] = "Vulnerable: __user pointer sanitization and usercopy barriers only; no swapgs barriers",
284+
[SPECTRE_V1_MITIGATION_AUTO] = "Mitigation: usercopy/swapgs barriers and __user pointer sanitization",
285+
};
286+
287+
static bool is_swapgs_serializing(void)
288+
{
289+
/*
290+
* Technically, swapgs isn't serializing on AMD (despite it previously
291+
* being documented as such in the APM). But according to AMD, %gs is
292+
* updated non-speculatively, and the issuing of %gs-relative memory
293+
* operands will be blocked until the %gs update completes, which is
294+
* good enough for our purposes.
295+
*/
296+
return boot_cpu_data.x86_vendor == X86_VENDOR_AMD;
297+
}
298+
299+
/*
300+
* Does SMAP provide full mitigation against speculative kernel access to
301+
* userspace?
302+
*/
303+
static bool smap_works_speculatively(void)
304+
{
305+
if (!boot_cpu_has(X86_FEATURE_SMAP))
306+
return false;
307+
308+
/*
309+
* On CPUs which are vulnerable to Meltdown, SMAP does not
310+
* prevent speculative access to user data in the L1 cache.
311+
* Consider SMAP to be non-functional as a mitigation on these
312+
* CPUs.
313+
*/
314+
if (boot_cpu_has(X86_BUG_CPU_MELTDOWN))
315+
return false;
316+
317+
return true;
318+
}
319+
320+
static void __init spectre_v1_select_mitigation(void)
321+
{
322+
if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1) || cpu_mitigations_off()) {
323+
spectre_v1_mitigation = SPECTRE_V1_MITIGATION_NONE;
324+
return;
325+
}
326+
327+
if (spectre_v1_mitigation == SPECTRE_V1_MITIGATION_AUTO) {
328+
/*
329+
* With Spectre v1, a user can speculatively control either
330+
* path of a conditional swapgs with a user-controlled GS
331+
* value. The mitigation is to add lfences to both code paths.
332+
*
333+
* If FSGSBASE is enabled, the user can put a kernel address in
334+
* GS, in which case SMAP provides no protection.
335+
*
336+
* [ NOTE: Don't check for X86_FEATURE_FSGSBASE until the
337+
* FSGSBASE enablement patches have been merged. ]
338+
*
339+
* If FSGSBASE is disabled, the user can only put a user space
340+
* address in GS. That makes an attack harder, but still
341+
* possible if there's no SMAP protection.
342+
*/
343+
if (!smap_works_speculatively()) {
344+
/*
345+
* Mitigation can be provided from SWAPGS itself or
346+
* PTI as the CR3 write in the Meltdown mitigation
347+
* is serializing.
348+
*
349+
* If neither is there, mitigate with an LFENCE.
350+
*/
351+
if (!is_swapgs_serializing() && !boot_cpu_has(X86_FEATURE_PTI))
352+
setup_force_cpu_cap(X86_FEATURE_FENCE_SWAPGS_USER);
353+
354+
/*
355+
* Enable lfences in the kernel entry (non-swapgs)
356+
* paths, to prevent user entry from speculatively
357+
* skipping swapgs.
358+
*/
359+
setup_force_cpu_cap(X86_FEATURE_FENCE_SWAPGS_KERNEL);
360+
}
361+
}
362+
363+
pr_info("%s\n", spectre_v1_strings[spectre_v1_mitigation]);
364+
}
365+
366+
static int __init nospectre_v1_cmdline(char *str)
367+
{
368+
spectre_v1_mitigation = SPECTRE_V1_MITIGATION_NONE;
369+
return 0;
370+
}
371+
early_param("nospectre_v1", nospectre_v1_cmdline);
372+
276373
#undef pr_fmt
277374
#define pr_fmt(fmt) "Spectre V2 : " fmt
278375

@@ -1290,7 +1387,7 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr
12901387
break;
12911388

12921389
case X86_BUG_SPECTRE_V1:
1293-
return sprintf(buf, "Mitigation: __user pointer sanitization\n");
1390+
return sprintf(buf, "%s\n", spectre_v1_strings[spectre_v1_mitigation]);
12941391

12951392
case X86_BUG_SPECTRE_V2:
12961393
return sprintf(buf, "%s%s%s%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],

0 commit comments

Comments
 (0)